diff --git a/docs/theme-preview.mdx b/docs/theme-preview.mdx index bc7a03dd..8f7d805e 100644 --- a/docs/theme-preview.mdx +++ b/docs/theme-preview.mdx @@ -30,10 +30,10 @@ By default level 2-3 headings generate the TOC on the top right ### Level 3 heading -Some text within a section. [Here is a link](/theme-preview.mdx). +Some text within a section. [Here is a link](./theme-preview.mdx). And here is some `inline code` to show how it looks, even some -[`inline code with a link`](/theme-preview.mdx). +[`inline code with a link`](./theme-preview.mdx). #### Level 4 heading @@ -190,7 +190,7 @@ Stacklok Enterprise includes turnkey integrations for common identity providers. Instead of manually configuring OIDC, use the built-in Okta or Entra ID integration to map IdP groups directly to ToolHive roles and policy sets. -[Learn more about Stacklok Enterprise](/toolhive/enterprise). +[Learn more about Stacklok Enterprise](./toolhive/enterprise.mdx). ::: diff --git a/docs/toolhive/index.mdx b/docs/toolhive/index.mdx index 1de61de2..fa20463f 100644 --- a/docs/toolhive/index.mdx +++ b/docs/toolhive/index.mdx @@ -17,26 +17,12 @@ import ThemedImage from '@theme/ThemedImage'; }} title='ToolHive logo' height={'120'} + style={{ 'margin-top': '1rem' }} />
--- - - # What is ToolHive? ToolHive is an enterprise-grade open source (Apache 2.0) platform for running diff --git a/docusaurus.config.ts b/docusaurus.config.ts index 0e47e307..3e96d34c 100644 --- a/docusaurus.config.ts +++ b/docusaurus.config.ts @@ -175,6 +175,24 @@ const config: Config = { keywords: ['enterprise'], extendDefaults: true, }, + lastVersion: 'current', + versions: { + current: { + label: 'Latest (OSS)', + }, + '1.1': { + label: 'Enterprise 1.1', + path: 'enterprise/1.1', + banner: 'none', + noIndex: true, + }, + '1.0': { + label: 'Enterprise 1.0', + path: 'enterprise/1.0', + banner: 'none', + noIndex: true, + }, + }, }, blog: { blogTitle: 'ToolHive Updates and Announcements', @@ -246,32 +264,34 @@ const config: Config = { position: 'left', items: [ { + type: 'doc', + docId: 'toolhive/index', label: 'Home', - href: '/toolhive', - }, - { - label: 'Integrations', - to: 'toolhive/integrations', }, { + type: 'doc', + docId: 'toolhive/guides-ui/index', label: 'ToolHive UI', - to: 'toolhive/guides-ui', }, { + type: 'doc', + docId: 'toolhive/guides-cli/index', label: 'ToolHive CLI', - to: 'toolhive/guides-cli', }, { + type: 'doc', + docId: 'toolhive/guides-k8s/index', label: 'Kubernetes Operator', - to: 'toolhive/guides-k8s', }, { + type: 'doc', + docId: 'toolhive/guides-vmcp/index', label: 'Virtual MCP Server', - to: 'toolhive/guides-vmcp', }, { - label: 'ToolHive Registry', - to: 'toolhive/guides-registry', + type: 'doc', + docId: 'toolhive/guides-registry/index', + label: 'Registry Server', }, ], }, @@ -281,28 +301,34 @@ const config: Config = { position: 'left', items: [ { + type: 'doc', + docId: 'toolhive/reference/cli/thv', label: 'ToolHive CLI commands', - to: 'toolhive/reference/cli/thv', }, { + type: 'doc', + docId: 'toolhive/reference/api', label: 'ToolHive API', - to: 'toolhive/reference/api', }, { + type: 'doc', + docId: 'toolhive/reference/crd-spec', label: 'ToolHive Operator CRD', - to: 'toolhive/reference/crd-spec', }, { + type: 'doc', + docId: 'toolhive/reference/registry-api', label: 'ToolHive Registry Server API', - to: 'toolhive/reference/registry-api', }, { + type: 'doc', + docId: 'toolhive/reference/registry-schema-toolhive', label: 'ToolHive registry schema', - to: 'toolhive/reference/registry-schema-toolhive', }, { + type: 'doc', + docId: 'toolhive/reference/registry-schema-upstream', label: 'Upstream registry schema', - to: 'toolhive/reference/registry-schema-upstream', }, ], }, @@ -312,9 +338,16 @@ const config: Config = { position: 'left', }, { - to: 'toolhive/enterprise', + type: 'doc', + docId: 'toolhive/enterprise', label: 'Enterprise', position: 'left', + className: 'navbar-doc-link', + }, + { + type: 'docsVersionDropdown', + position: 'right', + className: 'version-dropdown', }, { href: 'https://github.com/stacklok', diff --git a/eslint.config.mjs b/eslint.config.mjs index 8697d0a0..09fed9d5 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -52,7 +52,7 @@ export default [ // Config for _partials with JSX props { - files: ['docs/**/_partials/*.mdx'], + files: ['docs/**/_partials/*.mdx', 'versioned_docs/**/_partials/*.mdx'], rules: { 'no-unused-expressions': 'off', '@typescript-eslint/no-unused-expressions': 'off', diff --git a/proposals/docs-versioning-strategy.md b/proposals/docs-versioning-strategy.md new file mode 100644 index 00000000..f6141774 --- /dev/null +++ b/proposals/docs-versioning-strategy.md @@ -0,0 +1,325 @@ +# Docs versioning strategy proposal + +## Status + +Draft - April 2026 + +## Problem statement + +Stacklok is introducing Stacklok Enterprise, an enterprise +distribution of ToolHive that will be semantically versioned with +support for N-1 and N-2 minor releases. The ToolHive open source +(OSS) documentation currently follows a rolling "latest" model with +no versioning. We need a strategy that: + +- Gives Enterprise customers pinned, version-accurate documentation +- Keeps OSS documentation on a rolling latest without friction +- Presents Enterprise features inline with OSS docs as an upsell + opportunity +- Handles auto-generated reference content (CLI reference, CRD specs, + API specs) that is produced upstream +- Minimizes maintenance burden across active versions + +## Current state + +- Single unversioned Docusaurus docs instance at `/` +- ~96 auto-generated CLI reference pages, 5 API/schema spec files, + and 1 CRD spec page - all generated upstream +- Enterprise landing page exists at `/toolhive/enterprise` with + feature comparison and CTAs +- No enterprise-specific inline content in existing docs +- No Docusaurus versioning configured + +## Proposed approach + +### Versioning model + +Use Docusaurus's built-in docs versioning with the following mapping: + +| Audience | Docs state | URL path | +| ---------------------------------- | -------------------------------------- | ------------------------------ | +| OSS users | Rolling latest (unversioned "current") | `/toolhive/...` (default) | +| Enterprise users on latest release | Most recent versioned snapshot | `/enterprise/1.2/toolhive/...` | +| Enterprise users on older releases | Older versioned snapshots | `/enterprise/1.1/toolhive/...` | + +- **"Current"** always tracks the OSS rolling latest. This is the + default path and what search engines index. +- **Versioned snapshots** are cut on each Enterprise minor release + (e.g., 1.0, 1.1, 1.2). +- **Prune policy:** remove versions older than N-2. At any given time, + at most 3 Enterprise versions are live (current release + N-1 + + N-2). + +Docusaurus configuration (validated in spike): + +```ts title="docusaurus.config.ts (docs plugin options)" +lastVersion: 'current', +versions: { + current: { + label: 'Latest (OSS)', + }, + '1.1': { + label: 'Enterprise 1.1', + path: 'enterprise/1.1', + banner: 'none', + noIndex: true, + }, +}, +``` + +- `lastVersion: 'current'` keeps the `docs/` folder as the + default, served at the root route base path (`/`). No change to + existing OSS URLs. +- `path: 'enterprise/1.1'` routes the versioned snapshot under the + `/enterprise/1.1/` prefix instead of the default `/1.1/`. +- `banner: 'none'` suppresses the "unmaintained version" banner + (not appropriate for an Enterprise pinned version). +- `noIndex: true` prevents search engines from indexing versioned + pages, avoiding duplicate content with the OSS latest. + +A `docsVersionDropdown` navbar item renders the version picker. + +**Constraint:** the `versions` config block must not reference a +version until after `docs:version` has been run and `versions.json` +contains it. Docusaurus validates at startup and errors on unknown +versions. The version cut and config update must happen in +sequence. + +### Enterprise content inline + +Enterprise-specific content lives inline alongside OSS docs to serve +as a natural upsell. Two patterns: + +**Enterprise-only pages:** added to the docs tree like any other page +and included in every versioned snapshot. Examples based on current +Enterprise differentiation: + +- Configuration server setup and administration (enterprise lockdown + controls for the Desktop UI) +- Turnkey IdP integration guides (Okta, Entra ID) - distinct from + the generic OIDC/OAuth auth docs in OSS +- Enterprise Cloud UI administration (the full CRUD management + console) +- Enterprise Connectors catalog and qualification guides +- Canonical policy packs (pre-built read-only, full CRUD, and custom + Cedar policy sets) + +**Enterprise-specific sections within existing pages:** marked with a +custom admonition or component. These appear inline in OSS docs as a +natural upsell, showing Enterprise users what's available and giving +OSS users a reason to upgrade. Examples: + +```mdx title="In the Desktop UI guide" +:::enterprise + +The Enterprise distribution of the Desktop UI includes a +**configuration server** that lets administrators enforce lockdown +controls across your organization - restricting which MCP servers +can be installed, enforcing approved registries, and managing +update policies centrally. + +[Learn more about Stacklok Enterprise](/toolhive/enterprise). + +::: +``` + +```mdx title="In the auth/identity guide" +:::enterprise + +Stacklok Enterprise includes turnkey integrations for common +identity providers. Instead of manually configuring OIDC, use the +built-in Okta or Entra ID integration to map IdP groups directly +to ToolHive roles and policy sets. + +[Learn more about Stacklok Enterprise](/toolhive/enterprise). + +::: +``` + +```mdx title="In the MCP server management guide" +:::enterprise + +Enterprise Connectors are production-ready MCP servers maintained +on the Enterprise release cadence, built on hardened base images +(Chainguard or equivalent), signed with SigStore, and qualified +for target workloads. They include backported security patches and +SBOM/dependency vetting. + +[Learn more about Stacklok Enterprise](/toolhive/enterprise). + +::: +``` + +Both patterns snapshot cleanly with Docusaurus versioning - no special +handling needed at version cut time. + +### Auto-generated reference content + +Auto-generated content (CLI reference, API specs, CRD specs) is +produced upstream and pulled into the docs repo on release. + +**CLI reference and API specs:** these are already pulled in per +release. When an Enterprise version is cut, the snapshot captures +whatever reference docs are current at that point. + +**CRD specs:** use separate pages per CRD API version (e.g., +`crd-spec-v1alpha1.md`, `crd-spec-v1beta1.md`) since the upstream +generation has limited customization options and CRD versions may +coexist. + +### Version cut process + +Enterprise development continuously syncs and pins the latest +upstream releases. When an Enterprise release is cut, the pinned +upstream versions are typically the latest available. This means +`main` already reflects the correct component versions at cut +time, and the snapshot can usually be taken directly from `main` +HEAD. + +See [enterprise-version-cut-process.md](enterprise-version-cut-process.md) +for the full worked example and validated process. + +**Happy path (most releases):** + +1. Verify all pinned-version docs PRs have merged to `main`. +2. Add enterprise overlay content to `main` (enterprise-only + pages, `:::enterprise` admonitions). +3. Run `npx docusaurus docs:version ` on `main`. +4. Add the version-specific Docusaurus config. +5. Prune versions older than N-2. +6. Commit and deploy via PR. + +**Fallback - release branch:** if a component is rolled back to an +older version (e.g., a late regression in v0.23.0 forces a pin to +v0.22.0 after v0.23.0 docs have already landed), a release branch +from the right point in `main`'s history is needed. See the +process doc for the branch-based approach. + +This is a quality forcing function, not a guarantee of perfection. +The goal is "accurate enough" at cut time, with the understanding +that narrative docs may have minor drift. + +**Upstream requirement:** this process depends on each pinned +component version being fully documented before the cut. The +release-triggered docs PR for each component version is the +completeness gate - see the process doc for details. + +### Automated docs pipeline integration + +A parallel initiative is introducing automated, release-triggered docs +PRs: when an OSS component release is cut, a docs PR is generated +with an AI-drafted changelog and review required from contributors +on that release. + +This creates a natural per-component checkpoint. When an Enterprise +release is cut (comprising specific versions of each OSS component), +the docs will have already been reviewed in the context of each +constituent release. This significantly reduces the accuracy gap at +Enterprise version cut time. + +The release-triggered docs PR also serves as a potential automation +point for the version snapshot itself - the Enterprise release +pipeline could trigger the `docs:version` command after all component +docs PRs are merged. + +## Release cadence impact + +| Cadence | Active versions | Max drift (N-2 to latest) | Maintenance load | +| --------- | --------------- | ------------------------- | ------------------------------------------------------ | +| Monthly | 3 | ~2 months | Low - minimal divergence between versions | +| Quarterly | 3 | ~6 months | Moderate - guides and UI docs may diverge meaningfully | + +Starting monthly and moving to quarterly as the product stabilizes. +The quarterly cadence is where cross-version maintenance becomes a +real concern - at that point, the automated docs pipeline and +per-release review process will be critical for keeping versions +accurate. + +## Risks and mitigations + +**Maintenance burden of versioned snapshots.** Each snapshot is a full +copy of every doc page. A typo fix or security correction must be +patched in up to 4 places (current + 3 versions). + +- _Mitigation:_ Prune at N-2 to limit active versions. Automate + cherry-picking of critical fixes to active versions. Accept that + non-critical fixes only go into current. + +**Docs not matching Enterprise release at cut time.** The Enterprise +release pins specific upstream versions, but docs may describe +features not yet in that release. + +- _Mitigation:_ Release-triggered docs PRs reduce the gap. A + lightweight review checklist at cut time catches obvious issues. + Perfect accuracy is a non-goal - "useful and mostly correct" is the + bar. + +**Search engine indexing of versioned pages.** Duplicate content +across versions can dilute SEO. + +- _Mitigation:_ Set `noIndex: true` in the version config (validated + in spike). Docusaurus also sets canonical URLs to the latest + version by default. Verify both are working correctly after the + first production version cut. + +**Build time and repo size growth.** Each version snapshot adds a full +copy of the docs directory. + +- _Mitigation:_ Prune at N-2. Monitor build times after first few + cuts. If repo size becomes an issue, consider moving versioned docs + to a separate branch or git subtree. + +## Implementation phases + +### Phase 1: Foundation + +- Add the custom `:::enterprise` admonition component. +- Create separate CRD spec pages per API version + (`crd-spec-v1alpha1.md`, `crd-spec-v1beta1.md`). +- Add initial enterprise-specific inline content to existing pages. + +### Phase 2: First version cut + +- Follow the release branch process to cut the first Enterprise + version snapshot (see + [enterprise-version-cut-process.md](enterprise-version-cut-process.md)). +- Add the `lastVersion`, `versions`, and `docsVersionDropdown` + config to `docusaurus.config.ts` (config validated in spike - + see Docusaurus configuration above). +- Suppress the default version banner with `banner: 'none'`. +- Verify URL structure (`/enterprise//toolhive/...`), + `noIndex` on versioned pages, and version dropdown navigation. + +### Phase 3: Automation + +- Integrate version cut into Enterprise release pipeline. +- Automate pruning of versions older than N-2. +- Connect release-triggered docs PRs to version snapshot process. + +## Open questions + +- **Version label format:** `1.0`, `v1.0`, or `2026.04`? Should align + with the Enterprise product versioning scheme. +- **Version switcher UX:** ~~Should Enterprise users see a version + dropdown in the navbar, or is URL-based navigation sufficient?~~ + **Decision: navbar version picker.** The dropdown serves double duty + as a wayfinding indicator for Enterprise users on pinned versions and + as a discovery/upsell moment for OSS users who see Enterprise + versions listed. Docusaurus supports this natively via the + `docsVersionDropdown` navbar item. +- **Backport policy:** Which types of doc fixes warrant backporting to + older versions? Suggest limiting to factual errors and security + content. +- **Enterprise content gating:** ~~Is the inline enterprise content + purely informational (visible to all, with CTA), or should any of + it be gated behind authentication?~~ **Decision: no gating.** All + documentation, including Enterprise-specific content, is public. + No plans for gated material on the docs site. +- **Version persistence across non-docs pages:** Docusaurus version + context is purely URL-based. When an Enterprise user navigates to + a non-docs page (blog, external link) and returns, their version + resets to Latest (OSS). A localStorage or cookie-based solution + could remember the last-viewed version and restore it on return. + The version dropdown is hidden on non-docs pages (e.g., blog) via + CSS to avoid confusion, but the context loss on return is still a + UX papercut. diff --git a/proposals/enterprise-version-cut-process.md b/proposals/enterprise-version-cut-process.md new file mode 100644 index 00000000..4b2b87fe --- /dev/null +++ b/proposals/enterprise-version-cut-process.md @@ -0,0 +1,455 @@ +# Enterprise version cut process + +Companion to +[docs-versioning-strategy.md](docs-versioning-strategy.md) - +walks through the concrete process of cutting a versioned docs +snapshot when an Enterprise release is declared. + +## Status + +Draft - April 2026 + +## Upstream components and their docs PRs + +Each independently-versioned upstream component has a +release-triggered docs PR workflow. When a component cuts a release, +a docs PR is generated containing updated reference content and an +AI-drafted changelog for human review. + +| Component repo | What it covers | Example release | +| -------------------------- | ----------------------------- | --------------- | +| `toolhive` | CLI, API, Kubernetes Operator | v0.21.0 | +| `toolhive-studio` | Desktop UI app | v0.32.1 | +| `toolhive-registry-server` | Registry Server | v1.3.2 | +| `toolhive-cloud-ui` | Cloud portal | v0.7.2 | + +Each of these repos produces a docs PR against this docs-website +repo on release. Those PRs update: + +- Auto-generated CLI reference pages (toolhive) +- API and CRD specs (toolhive) +- Feature documentation and guides (all components) +- Changelog/release notes content (all components) + +## The Enterprise release + +The `stacklok-enterprise` repo consolidates the upstream components +at pinned versions and adds enterprise overlays: + +- Enterprise Connectors (qualified MCP servers) +- Configuration server (Desktop UI lockdown controls) +- Turnkey IdP integrations (Okta, Entra ID) +- Canonical policy packs +- Enterprise Cloud UI administration + +An Enterprise release declares a specific version of each upstream +component. For this walkthrough, we're cutting **Enterprise v1.1**. + +## How Enterprise pins upstream versions + +Enterprise development continuously syncs and pins the latest +upstream releases. The process is not "declare versions, then +build" - it's more like: + +1. Enterprise features are developed iteratively. +2. During that process, Enterprise continuously syncs and pins + upstream releases. +3. When an Enterprise release is cut, the pinned upstream versions + are the latest (or very recent) available. + +This means that at Enterprise release time, the docs on `main` +should already reflect the pinned component versions because +they're the latest. The happy path is to snapshot `main` HEAD +directly. + +### When does forward drift happen? + +In rare cases, a component may be rolled back after its docs have +already landed. For example, a late-breaking regression in +toolhive v0.23.0 might force the Enterprise release to pin +v0.22.0 instead, but the v0.23.0 docs PR has already merged to +`main`. In this scenario, `main` documents features the Enterprise +customer doesn't have. + +When this happens, a release branch from the right point in +`main`'s history is needed (see the fallback process below). + +## Upstream requirement: docs completeness + +Whether the snapshot is taken from `main` HEAD (happy path) or +from a release branch (fallback), the version cut captures whatever +docs exist at that point. If a pinned component's docs PR was +merged but incomplete - for example, a new CLI command was added +in toolhive v0.21.0 but the docs PR only updated the +auto-generated reference and skipped the narrative guide - that gap +is baked into the Enterprise snapshot. + +This means the release-triggered docs PR for each component +version must be treated as a completeness gate, not just a +reference-update mechanism. Reviewers on each docs PR need to +verify that: + +- Auto-generated content (CLI reference, API specs, CRD specs) is + present and correct. +- New features, changed behaviors, and deprecations are reflected + in the relevant guides and concept pages. +- Breaking changes have migration guidance. + +The quality bar for these docs PRs directly determines the quality +of the Enterprise snapshot. If the upstream docs PR process is +treated as a "we'll fix it later" checkpoint, the Enterprise +version cut inherits that debt with no easy way to pay it down +(since the snapshot is a frozen copy). + +For a monthly Enterprise release cadence, this is manageable: the +window between component releases and the Enterprise cut is short, +and docs PRs are small. For a quarterly cadence, the risk +increases: more features accumulate per component release, docs PRs +are larger and take longer to review, and the pressure to cut the +Enterprise release on schedule may conflict with docs completeness. + +## Worked example: cutting Enterprise v1.1 + +### Enterprise v1.1 manifest + +```text +stacklok-enterprise v1.1 +├── toolhive v0.21.0 +├── toolhive-studio v0.32.1 +├── toolhive-registry-server v1.3.2 +└── toolhive-cloud-ui v0.7.2 +``` + +### Happy path: snapshot from main HEAD + +Since Enterprise pins the latest upstream at cut time, `main` +should already reflect all four pinned versions. The typical state +at cut time: + +```text +main branch (HEAD) +────────────────────────────────────────────────── + + merge: toolhive v0.21.0 docs PR ◄── pinned (latest) + merge: toolhive-studio v0.32.1 docs PR ◄── pinned (latest) + merge: registry-server v1.3.2 docs PR ◄── pinned (latest) + merge: cloud-ui v0.7.2 docs PR ◄── pinned (latest) + merge: misc unrelated docs fixes + ← HEAD +``` + +`main` HEAD matches the Enterprise manifest. Snapshot directly. + +#### 1. Verify all pinned-version docs PRs are merged + +Confirm that the release-triggered docs PR for each component +version in the manifest has been merged into `main`. + +Checklist: + +- [ ] `toolhive` v0.21.0 docs PR - merged +- [ ] `toolhive-studio` v0.32.1 docs PR - merged +- [ ] `toolhive-registry-server` v1.3.2 docs PR - merged +- [ ] `toolhive-cloud-ui` v0.7.2 docs PR - merged + +If any pinned-version docs PR is still open or hasn't been +generated yet, that's a blocker. + +#### 2. Add enterprise overlay content + +Commit enterprise-specific content to `main`: + +- Enterprise-only pages (Configuration server guides, IdP + integration guides, Connectors catalog, policy pack docs, Cloud + UI administration). +- `:::enterprise` inline admonitions in existing OSS pages where + Enterprise v1.1 introduces new differentiation. +- Updates to the Enterprise landing page at `/toolhive/enterprise` + if the feature comparison or pricing has changed. + +This content serves double duty: it appears in the Enterprise +snapshot and stays in the OSS rolling latest as upsell content. + +#### 3. Sanity-check + +Quick review of the docs on `main` against the Enterprise v1.1 +manifest. Look for: + +- Features documented in latest that aren't in any of the pinned + component versions. +- Broken links or references to content that doesn't exist yet. +- Enterprise content that references capabilities not in this + release. + +#### 4. Cut the version snapshot + +```bash +npx docusaurus docs:version 1.1 +``` + +This creates: + +- `versioned_docs/version-1.1/` - a full copy of the `docs/` + directory +- `versioned_sidebars/version-1.1-sidebars.json` - a snapshot of + the sidebar configuration +- An entry in `versions.json` for version `1.1` + +#### 5. Update Docusaurus config + +After cutting the version, add the version-specific config to +`docusaurus.config.ts`: + +```ts title="docusaurus.config.ts (docs plugin options)" +lastVersion: 'current', +versions: { + current: { + label: 'Latest (OSS)', + }, + '1.1': { + label: 'Enterprise 1.1', + path: 'enterprise/1.1', + banner: 'none', + noIndex: true, + }, +}, +``` + +Key settings: + +- `lastVersion: 'current'` keeps the `docs/` folder (OSS rolling + latest) as the default at the root path. +- `path: 'enterprise/1.1'` puts the versioned docs under + `/enterprise/1.1/toolhive/...` instead of the default + `/1.1/toolhive/...`. +- `banner: 'none'` suppresses the default "this is an older + version" banner, which doesn't make sense for an Enterprise + pinned version. +- `noIndex: true` prevents search engines from indexing versioned + pages, avoiding duplicate content. + +Also add the version dropdown to the navbar (first version cut +only): + +```ts title="docusaurus.config.ts (navbar items)" +{ + type: 'docsVersionDropdown', + position: 'right', +}, +``` + +**Important:** the `versions` config block must not reference a +version until after `docs:version` has been run and `versions.json` +contains it. Docusaurus validates at startup and errors on unknown +versions. + +#### 6. Prune old versions if needed + +Check whether the N-2 threshold is exceeded. With Enterprise v1.1, +the active versions should be: + +| Version | Status | +| -------------------- | ----------------------------- | +| current (OSS latest) | Always active | +| 1.1 | Current Enterprise release | +| 1.0 | N-1 (supported) | +| 0.9 | N-2 (supported, if it exists) | + +If a version older than N-2 exists, remove it: + +```bash +# Remove the versioned docs directory +rm -rf versioned_docs/version-0.8 + +# Remove the versioned sidebar +rm versioned_sidebars/version-0.8-sidebars.json + +# Remove the entry from versions.json +# (edit manually or script it) +``` + +#### 7. Commit and deploy + +```bash +git add versioned_docs/version-1.1 \ + versioned_sidebars/version-1.1-sidebars.json \ + versions.json \ + docusaurus.config.ts +git commit -m "Cut Enterprise v1.1 docs snapshot" +``` + +Open a PR, review, merge, and deploy through the normal pipeline. + +### Fallback: release branch for component rollbacks + +If a component is rolled back to an older version after its +successor's docs have already landed on `main`, the happy path +doesn't work. For example, a late regression in toolhive v0.22.0 +forces a pin to v0.21.0, but the v0.22.0 docs PR has already +merged. + +```text +main branch (HEAD) +────────────────────────────────────────────────── + + merge: toolhive v0.21.0 docs PR ◄── pinned + merge: toolhive v0.22.0 docs PR ◄── AHEAD of pin + ← HEAD +``` + +In this case: + +1. Identify the base commit - the merge of the last pinned-version + docs PR before the newer version landed. +2. Create a release branch from that commit: + `git checkout -b enterprise/v1.1 ` +3. Add enterprise overlay content on the branch. +4. Cut the version and update config on the branch. +5. Copy the snapshot artifacts back to `main` via PR: + + ```bash + git checkout main + git checkout enterprise/v1.1 -- \ + versioned_docs/version-1.1 \ + versioned_sidebars/version-1.1-sidebars.json + ``` + +6. Update `versions.json` and `docusaurus.config.ts` on `main`. +7. Prune, commit, and deploy as normal. + +## Resulting URL structure + +After cutting Enterprise v1.1, the site serves: + +| URL path | Content | +| ------------------------------ | ------------------------------------------------------- | +| `/toolhive/...` | OSS rolling latest (default, indexed by search engines) | +| `/enterprise/1.1/toolhive/...` | Enterprise v1.1 snapshot | +| `/enterprise/1.0/toolhive/...` | Enterprise v1.0 snapshot (N-1) | + +The version dropdown in the navbar shows all active versions, +with the OSS latest as the default selection. + +## Validated behavior (spike) + +The following was validated on a local dev server using the +Docusaurus config changes described above: + +- **OSS latest stays at `/toolhive/...`** - `lastVersion: 'current'` + keeps the `docs/` folder as the default, served at the root + route base path. No change to existing OSS URLs. +- **Enterprise version at `/enterprise/1.1/toolhive/...`** - the + `path: 'enterprise/1.1'` config correctly routes the versioned + snapshot under the desired prefix. +- **Version dropdown** - the `docsVersionDropdown` navbar item + renders a dropdown showing "Latest (OSS)" and "Enterprise 1.1". + Clicking "Enterprise 1.1" navigates to `/enterprise/1.1/toolhive/`. + Clicking "Latest (OSS)" navigates back to `/toolhive/`. +- **Version badge** - a "Version: Enterprise 1.1" badge appears + below the breadcrumb on versioned pages, giving clear context. +- **No version banner** - `banner: 'none'` suppresses the default + "unmaintained version" banner. +- **Sidebar links** - all sidebar links on versioned pages + correctly use `/enterprise/1.1/toolhive/...` paths. +- **Config ordering constraint** - the `versions` block in the docs + plugin config must not reference a version until after + `docs:version` has been run and `versions.json` contains it. + Docusaurus fails at startup with a validation error otherwise. + +## Timeline + +### Happy path + +```text +Enterprise v1.1 version cut (happy path) +───────────────────────────────────────────────────────── + +main branch (time flows down) +│ +│ merge: toolhive v0.21.0 docs PR ◄── latest +│ merge: toolhive-studio v0.32.1 docs PR ◄── latest +│ merge: registry-server v1.3.2 docs PR ◄── latest +│ merge: cloud-ui v0.7.2 docs PR ◄── latest +│ ← HEAD (matches Enterprise v1.1 manifest) +│ +│ +│ Enterprise release declared +│ │ +│ ├─ 1. Verify all pinned docs PRs merged ✓ +│ ├─ 2. Add enterprise overlays to main +│ ├─ 3. Sanity-check +│ ├─ 4. npx docusaurus docs:version 1.1 +│ ├─ 5. Update docusaurus.config.ts +│ ├─ 6. Prune old versions +│ └─ 7. PR, review, merge, deploy +``` + +### Fallback (component rollback) + +```text +Enterprise v1.1 version cut (with rollback) +───────────────────────────────────────────────────────── + +main branch (time flows down) +│ +│ merge: toolhive v0.21.0 docs PR ◄── pinned +│ merge: toolhive v0.22.0 docs PR ◄── AHEAD (rollback) +│ ← HEAD +│ +│ Enterprise release declared (pins v0.21.0) +│ │ +│ ├─ 1. Identify base commit (v0.21.0 docs PR merge) +│ ├─ 2. Create branch: enterprise/v1.1 from base +│ │ │ +│ │ ├─ 3. Add enterprise overlays +│ │ ├─ 4. npx docusaurus docs:version 1.1 +│ │ └─ snapshot artifacts ready +│ │ +│ ├─ 5. Copy snapshot artifacts to main +│ ├─ 6. Prune old versions +│ └─ 7. PR, review, merge, deploy +``` + +## Automation opportunities + +The version cut can be partially or fully automated as part of the +Enterprise release pipeline in `stacklok-enterprise`: + +1. **Trigger:** Enterprise release pipeline reaches the "cut docs" + stage. The pipeline provides the component version manifest. +2. **Verify:** Script checks that all expected component docs PRs + (matching the versions in the manifest) have been merged to + `main` in the docs-website repo. If any are missing or still + open, the automation blocks and alerts. +3. **Check for drift:** Script compares the pinned versions in the + manifest against the latest component docs PRs on `main`. If + `main` is ahead of any pin (a component was rolled back), switch + to the release branch fallback. Otherwise, proceed with the + happy path on `main`. +4. **Cut:** Script runs `npx docusaurus docs:version ` on + `main` (happy path) or the release branch (fallback). +5. **Config:** Script updates `docusaurus.config.ts` with the new + version entry. +6. **PR to main:** Script opens a PR on docs-website with the + snapshot artifacts, config update, and any version pruning. +7. **Merge and deploy:** Human reviews and merges the PR. Vercel + deploys automatically. + +Step 2 (verify) is the key gate and depends on being able to +reliably identify which docs PR corresponds to which component +version - see open questions below. + +## Open questions + +- **Component docs PR identification:** How do we reliably match a + docs PR to a specific component version? This is critical for + the verification gate (and for the fallback, identifying the base + commit). Options include PR title conventions, labels, or a + manifest file in the PR. A structured label like + `component/toolhive/v0.21.0` would make automated lookups + straightforward. +- **Partial updates after cut:** If a critical docs fix lands on + `main` after a version is cut, do we re-cut the version or + cherry-pick into the `versioned_docs/` directory on `main`? The + strategy proposal suggests limiting backports to factual errors + and security content. diff --git a/src/css/custom.css b/src/css/custom.css index 151fab84..fbec6e92 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -168,6 +168,34 @@ thead th { color: var(--stacklok-white); } +/* + * Neutralize active-state styling on doc-type navbar items. + * Switching to type:'doc' for version-aware links causes Docusaurus + * to mark items as "active" whenever any doc page is viewed. Reset + * top-level items and dropdown items back to default appearance. + */ +.navbar-doc-link.navbar__link--active { + color: var(--ifm-navbar-link-color); +} + +.navbar-doc-link.navbar__link--active:hover { + color: var(--ifm-navbar-link-hover-color); +} + +.dropdown__link--active { + color: inherit !important; + background: transparent !important; +} + +.dropdown__link--active:hover { + background: var(--ifm-dropdown-hover-background-color) !important; +} + +/* Hide version dropdown on non-docs pages (e.g., blog) */ +html:not(.plugin-docs) .version-dropdown { + display: none; +} + /* Navbar logo */ .navbar__logo { display: flex; diff --git a/versioned_docs/version-1.0/theme-preview.mdx b/versioned_docs/version-1.0/theme-preview.mdx new file mode 100644 index 00000000..67de14ff --- /dev/null +++ b/versioned_docs/version-1.0/theme-preview.mdx @@ -0,0 +1,314 @@ +--- +title: Theme preview page +description: This is a page used to preview a lot of theme elements in one place. +displayed_sidebar: toolhiveSidebar +pagination_next: toolhive/guides-cli/install +pagination_prev: toolhive/index +--- + + + + + +This is a page used to preview a lot of theme elements in one place when working +on styles. + +[Community knowledge base for Docusaurus design tips](https://docusaurus.community/knowledge/design/) + +Breadcrumbs above aren't very interesting since this is a top-level page that +doesn't participate in a sidebar, navigate to a different page to see them. + +Another element that can't be easily reproduced here is the DocCardList +component, but you can see it in action on the +[ToolHive CLI guides index page](./toolhive/guides-cli/index.mdx). + +## Level 2 heading + +By default level 2-3 headings generate the TOC on the top right +([reference](https://docusaurus.io/docs/markdown-features/toc#table-of-contents-heading-level)). + +### Level 3 heading + +Some text within a section. [Here is a link](/theme-preview.mdx). + +And here is some `inline code` to show how it looks, even some +[`inline code with a link`](/theme-preview.mdx). + +#### Level 4 heading + +This level won't appear in the TOC by default. + +## Code blocks + +[Docusaurus reference docs](https://docusaurus.io/docs/markdown-features/code-blocks) + +```js title="Some JavaScript with line numbers" showLineNumbers +console.log('We love marmots.'); + +function MarmotsAreGreat(agree) { + if (agree) { + // highlight-next-line + return 'I agree, and this line is highlighted!'; + } + + return 'I am wrong.'; +} +``` + +```yaml title="some-yaml.yaml" +--- +# Sample profile for validating repositories +version: v1 +type: profile +name: acme-github-profile +display_name: Sample Profile +alert: 'off' +remediate: 'off' +repository: + - type: allowed_selected_actions + def: + github_owned_allowed: true + verified_allowed: true + patterns_allowed: [] +``` + +```json title="Example JSON with highlighted lines" {2,5-7} +{ + "key": "String", + "Number": 1, + "array": [1, 2, 3], + "nested": { + "literals": true + } +} +``` + +## Admonitions + +These are MDX callouts +([reference](https://docusaurus.io/docs/markdown-features/admonitions)). + +To customize the title, use square brackets after the type, e.g. +`:::tip[My title]`. + +To keep Prettier from invalidating the admonition syntax, add empty lines around +the start and end of the admonition block (see +[here](https://docusaurus.io/docs/markdown-features/admonitions#usage-with-prettier)). + +They can be customized in src/css/custom.css like so +([reference](https://docusaurus.community/knowledge/design/admonitions/#updating-the-css)): + +```css +/* Customize the "Tip" admonition */ +.alert--success { + --ifm-alert-background-color: #59cfa8; + --ifm-alert-background-color-highlight: #00bbbe26; + --ifm-alert-foreground-color: #002a3e; + --ifm-alert-border-color: #002a3e; +} + +/* Use a different border color in dark mode */ +[data-theme='dark'] .alert--success { + --ifm-alert-border-color: #008385; +} +``` + +:::note + +This is a `note` admonition. Its CSS class is `theme-admonition-note`. + +[Here's a link inside the admonition](#tables). + +::: + +:::tip + +This is a `tip` admonition. Its CSS class is `theme-admonition-tip`. + +[Here's a link inside the admonition](#tables). + +::: + +:::info[Hello] + +This is an `info` admonition. Its CSS class is `theme-admonition-info` and it +has a custom title. + +[Here's a link inside the admonition](#tables). + +::: + +:::warning + +This is a `warning` admonition. Its CSS class is `theme-admonition-warning`. + +[Here's a link inside the admonition](#tables). + +::: + +:::danger + +This is a `danger` admonition. Its CSS class is `theme-admonition-danger`. + +[Here's a link inside the admonition](#tables). + +::: + +:::::info[Parent] + +Admonitions can be nested; example here so we can see how the colors look +together. + +::::danger[Child] + +Child content + +:::tip[Inception] + +This is getting silly + +::: + +:::: + +::::: + +## Tables + +A standard Markdown table: + +| Column 1 | Column 2 | Column 3 | +| --------------- | :---------: | ---------------------------- | +| This | hello | [A link in a table](#tables) | +| That | hi | `value` | +| The other thing | how are you | 🙈 | +| Another row | so you | can see the zebra effect | + +(Docusaurus theme enables header row and zebra rows by default) + +## Tabs + +MDX Tabs component, default theme +([reference](https://docusaurus.io/docs/markdown-features/tabs)) + + + + This is an apple 🍎 + + ```python title="Code block inside a tab" + print('We love marmots.') + + def marmots_are_great(agree): + if agree: + # highlight-next-line + return 'I agree, and this line is highlighted!' + + return 'I am wrong.' + ``` + + + + This is an orange 🍊 + + + This is a banana 🍌 + + + +## Details panel + +
+ Click to expand + +This is a details panel, which can be used to show additional information +without cluttering the page. + +It can contain any Markdown or MDX content, including `inline code`, code +blocks, images, even other details panels. +[Here's a link inside the details panel](#details-panel). + +```js title="JavaScript code inside a details panel" +console.log('This is inside a details panel.'); +``` + +
+ +## Images + +An MDX ThemedImage, which switches based on light/dark theme +([reference](https://docusaurus.io/docs/markdown-features/assets#themed-images)) + +import useBaseUrl from '@docusaurus/useBaseUrl'; +import ThemedImage from '@theme/ThemedImage'; + + + +An image using the "screenshot" CSS class to add a border and shadow/glow to +make them stand out from the background: + + + +## Diagrams + +A mermaid flowchart +([reference](https://docusaurus.io/docs/markdown-features/diagrams)) + +```mermaid +flowchart LR + node1["Node"] -- Label --> node2 + node2["Node"] -- Label --> node3 + subgraph container["**Subgraph**"] + direction LR + subgraph container1["Nested subgraph"] + node3["Node"] + end + end +``` + +A mermaid sequence diagram + +```mermaid +sequenceDiagram + Alice->>+John: Hello John, how are you? + note right of John: Note + John-->>-Alice: Great! + Alice-)John: See you later! +``` + +## Other standard elements + +Here's some **bold** and _italic_ text. + +Unordered list: + +- One +- Two +- Three + +Ordered list: + +1. One +1. Two +1. Three + +Horizontal line: + +--- + +## Pagination diff --git a/versioned_docs/version-1.0/toolhive/_partials/_auth-troubleshooting.mdx b/versioned_docs/version-1.0/toolhive/_partials/_auth-troubleshooting.mdx new file mode 100644 index 00000000..fb41b43e --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/_partials/_auth-troubleshooting.mdx @@ -0,0 +1,42 @@ +
+Authentication issues + +If clients can't authenticate: + +1. Check that the JWT token is valid and not expired +2. Verify that the audience and issuer match your configuration +3. Ensure the JWKS URL is accessible +4. Check the server logs for specific authentication errors: + + ```bash + # View logs + thv logs + + # Follow logs in real-time (like tail -f) + thv logs --follow + + # View proxy logs instead of container logs + thv logs --proxy + ``` + +
+ +
+Authorization issues + +If authenticated clients are denied access: + +1. Make sure your Cedar policies explicitly permit the specific action + (remember, default deny) +2. Check that the principal, action, and resource match what's in your policies + (including case and formatting) +3. Examine any conditions in your policies to ensure they're satisfied (for + example, required JWT claims or tool arguments) +4. Remember that Cedar uses a default deny policy—if no policy explicitly + permits an action, it will be denied + +**Troubleshooting tip:** If access is denied, check that your policies +explicitly permit the action. Cedar uses a default deny model—if no policy +matches, the request is denied. + +
diff --git a/versioned_docs/version-1.0/toolhive/_partials/_basic-cedar-config.mdx b/versioned_docs/version-1.0/toolhive/_partials/_basic-cedar-config.mdx new file mode 100644 index 00000000..86f6b33b --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/_partials/_basic-cedar-config.mdx @@ -0,0 +1,35 @@ +Create a JSON or YAML file with Cedar policies. This example demonstrates +several policy patterns: + +- Allow everyone to use the weather tool +- Restrict the admin_tool to a specific user (alice123) +- Role-based access: only users with the "premium" role can call any tool +- Attribute-based: allow the calculator tool only for add/subtract operations + +Here's an example in JSON format: + +```json +{ + "version": "1.0", + "type": "cedarv1", + "cedar": { + "policies": [ + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"weather\");", + "permit(principal == Client::\"alice123\", action == Action::\"call_tool\", resource == Tool::\"admin_tool\");", + "permit(principal, action == Action::\"call_tool\", resource) when { principal.claim_roles.contains(\"premium\") };", + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"calculator\") when { resource.arg_operation == \"add\" || resource.arg_operation == \"subtract\" };" + ], + "entities_json": "[]" + } +} +``` + +You can also define custom resource attributes in `entities_json` for per-tool +ownership or sensitivity labels. + +:::tip + +For more policy examples and advanced usage, see +[Cedar policies](../concepts/cedar-policies.mdx). + +::: diff --git a/versioned_docs/version-1.0/toolhive/_partials/_client-config-intro.mdx b/versioned_docs/version-1.0/toolhive/_partials/_client-config-intro.mdx new file mode 100644 index 00000000..0d0eeefb --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/_partials/_client-config-intro.mdx @@ -0,0 +1,17 @@ +ToolHive automatically configures supported AI clients to work with MCP servers. +This guide shows you how to set up and manage client configurations. + +## Understanding client configuration + +Before an AI application can use ToolHive MCP servers, it needs to know where to +find them. You can configure clients in two ways: + +1. **{props.term} a supported client**: {props.term} your client with ToolHive + so it automatically manages and updates the client's configuration as you + start, stop, and remove MCP servers. +2. **Manual configuration**: For clients that ToolHive doesn't directly support, + manually configure them to connect to ToolHive-managed MCP servers using the + SSE or Streamable HTTP protocol. + +For a complete list of supported clients and compatibility details, see the +[Client compatibility reference](../reference/client-compatibility.mdx). diff --git a/versioned_docs/version-1.0/toolhive/_partials/_oidc-prerequisites.mdx b/versioned_docs/version-1.0/toolhive/_partials/_oidc-prerequisites.mdx new file mode 100644 index 00000000..99e1cb66 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/_partials/_oidc-prerequisites.mdx @@ -0,0 +1,22 @@ +Before you begin, make sure you have: + +- ToolHive installed and working +- Basic familiarity with OAuth, OIDC, and JWT concepts +- An identity provider that supports OpenID Connect (OIDC), such as Google, + GitHub, Microsoft Entra ID (Azure AD), Okta, Auth0, or Kubernetes (for service + accounts) + +From your identity provider, you'll need: + +- Client ID +- Audience value +- Issuer URL +- JWKS URL (for key verification) + +ToolHive uses OIDC to connect to your existing identity provider, so you can +authenticate with your own credentials (for example, Google login) or with +service account tokens (for example, in Kubernetes). ToolHive never sees your +password, only signed tokens from your identity provider. + +For background on authentication, authorization, and Cedar policy examples, see +[Authentication and authorization framework](../concepts/auth-framework.mdx). diff --git a/versioned_docs/version-1.0/toolhive/_partials/_remote-mcp-auth-examples.mdx b/versioned_docs/version-1.0/toolhive/_partials/_remote-mcp-auth-examples.mdx new file mode 100644 index 00000000..af1b84c6 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/_partials/_remote-mcp-auth-examples.mdx @@ -0,0 +1,82 @@ +#### Remote MCP server with Auto-Discovered authentication + +**Auto-Discovered** authentication is the preferred approach for any MCP server +that supports it, as ToolHive handles all authentication setup automatically +with minimal configuration required. Notion's remote MCP server is one example +that supports this feature: + +1. Configuration settings: + - **Server name**: `notion-remote` + - **Server URL**: `https://mcp.notion.com/mcp` + - **Transport**: Streamable HTTP + - **Authorization method**: Auto-Discovered + - **Callback port**: `45673` (or any available port on your system) +1. When you install the server, ToolHive discovers the OAuth endpoints, + registers a new client, and handles the authentication process. +1. Your browser opens for authentication. After you authorize access, the remote + MCP server appears in your server list with a "Running" status. + +#### Remote MCP server with OAuth2 authentication + +GitHub's remote MCP server requires manual OAuth configuration. You'll need to +create a GitHub OAuth app and provide the details in ToolHive. + +First, create a GitHub OAuth app: + +1. Go to [GitHub Developer Settings](https://github.com/settings/developers) +1. Click **New OAuth App** +1. Fill in the application details: + - **Application name**: Choose a descriptive name (e.g., "ToolHive GitHub + MCP") + - **Homepage URL**: Your application's homepage or `http://localhost` + - **Authorization callback URL**: `http://localhost:45673/callback` (the port + number must match the **Callback port** you will enter in ToolHive) +1. Click **Register application** +1. Copy the **Client ID** and generate a **Client secret** for use in ToolHive + +Configure the remote MCP server in ToolHive: + +1. Configuration settings: + - **Server name**: `github-remote` + - **Server URL**: `https://api.githubcopilot.com/mcp/` + - **Transport**: Streamable HTTP + - **Authorization method**: OAuth 2.0 + - **Callback port**: `45673` (or any available port on your system) + - **Authorize URL**: `https://github.com/login/oauth/authorize` + - **Token URL**: `https://github.com/login/oauth/access_token` + - **Client ID**: Your GitHub OAuth app client ID (e.g., + `Og44jirLIaUgSiTDNGA3`) + - **Client secret**: Your GitHub OAuth app client secret (optional if PKCE is + enabled) + - **Scopes**: `repo,user:email` (comma-separated list of required + permissions) + - **PKCE**: Enable this option for enhanced security without requiring a + client secret +1. When you install the server, ToolHive opens your browser to authenticate with + GitHub and authorize the application. +1. After you authenticate successfully, the remote MCP server appears in your + server list with a "Running" status. + +#### Remote MCP server with OIDC authentication + +GitHub's remote MCP server also supports OIDC authentication, which provides +additional security features and standardized token handling. Use the same +GitHub OAuth app from the previous example. + +1. Fill in the configuration form: + - **Server name**: `github-remote` + - **Server URL**: `https://api.githubcopilot.com/mcp/` + - **Transport**: Streamable HTTP + - **Authorization method**: OIDC + - **Callback port**: `45673` (or any available port on your system) + - **Issuer URL**: `https://github.com/login/oauth` + - **Client ID**: Your GitHub OAuth app client ID (e.g., + `Og44jirLIaUgSiTDNGA3`) + - **Client secret**: Your GitHub OAuth app client secret (optional if PKCE is + enabled) + - **PKCE**: Enable this option for enhanced security without requiring a + client secret +1. When you install the server, ToolHive opens your browser to authenticate with + GitHub using OIDC. +1. After you authenticate successfully, the remote MCP server appears in your + server list with a "Running" status. diff --git a/versioned_docs/version-1.0/toolhive/concepts/auth-framework.mdx b/versioned_docs/version-1.0/toolhive/concepts/auth-framework.mdx new file mode 100644 index 00000000..cc8286a8 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/concepts/auth-framework.mdx @@ -0,0 +1,347 @@ +--- +title: Authentication and authorization +description: Understanding ToolHive's authentication and authorization framework concepts. +--- + +This document explains the concepts behind ToolHive's authentication and +authorization framework, which secures MCP servers by verifying client identity +and controlling access to resources. You'll learn how these systems work +together, the reasoning behind their design, and the benefits of this approach. + +:::info[Scope of this documentation] + +This documentation covers **client-to-MCP-server authentication**—how clients +authenticate to the MCP server itself. This is about securing access to the MCP +server's tools and resources. + +This is different from **MCP-server-to-backend authentication**, which involves +how the MCP server authenticates to external services or APIs it calls (for +example, a GitHub MCP server authenticating to the GitHub API). That topic is +covered in [Backend authentication](./backend-auth.mdx). + +::: + +## Understanding authentication vs. authorization + +When you secure MCP servers, you need to understand the strong separation +between two critical security concepts: + +- **Authentication (authN):** Verifying the identity of clients connecting to + your MCP server ("Who are you?") +- **Authorization (authZ):** Determining what actions authenticated clients are + allowed to perform ("What can you do?") + +You should always perform authentication first, using a trusted identity +provider, and then apply authorization rules to determine what the authenticated +identity can do. ToolHive helps you follow this best practice by acting as a +gateway in front of your MCP servers. This approach lets you use proven identity +systems for authentication, while keeping your authorization policies clear, +flexible, and auditable. You don't need to add custom authentication or +authorization logic to every server—ToolHive handles it for you, consistently +and securely. + +## Why ToolHive centralizes authentication + +The +[official MCP specification](https://modelcontextprotocol.io/docs/tutorials/security/authorization) +recommends OAuth 2.1-based authorization for HTTP transports, where each MCP +server acts as an OAuth resource server. In practice, this model creates +significant operational challenges: + +- **OAuth client registration burden:** OAuth 2.0 requires pre-registered + redirect URIs at each identity provider. Many providers—such as Google, + GitHub, and Atlassian—require manual registration of OAuth clients to obtain a + client ID and client secret. If each user client (for example, an IDE) were + its own OAuth client, the registration burden would be impractical at scale. +- **No federation with external services:** While token exchange (RFC 8693) and + federated identity providers work when the upstream service is in the same + trust domain as the MCP server or has an established trust relationship with + the identity provider, many MCP servers need to access external services like + GitHub, Google, or Atlassian APIs where no federation relationship exists. +- **Per-server implementation cost:** Each MCP server would need to implement + its own token validation and scope management, duplicating security-critical + logic across servers. + +ToolHive addresses the per-server implementation cost by centralizing +authentication and authorization in its proxy layer. You configure ToolHive with +your identity provider and write Cedar policies for fine-grained +authorization—individual MCP servers don't need to implement token validation or +scope management. + +The [embedded authorization server](#embedded-authorization-server) addresses +the remaining challenges. It exposes standard OAuth endpoints and handles the +full OAuth web flow, eliminating the client registration burden through Dynamic +Client Registration (DCR) and solving the federation gap by obtaining tokens +directly from external providers like GitHub or Atlassian. ToolHive delegates +authentication to the upstream provider and issues its own tokens, giving MCP +clients a spec-compliant OAuth experience while centralizing the complexity of +token acquisition and management. + +## Authentication framework + +ToolHive uses OAuth-based authentication with support for both OAuth 2.1 and +OpenID Connect (OIDC), enabling both JWT tokens and opaque token validation. +This lets you connect ToolHive to any OAuth 2.1 or OIDC-compliant identity +provider (IdP), such as Google, GitHub, Microsoft Entra ID (Azure AD), Okta, +Auth0, or even Kubernetes service accounts. ToolHive never handles your raw +passwords or credentials; instead, it relies on access tokens issued by your +trusted provider—either self-contained JWT tokens or opaque tokens validated +through token introspection. + +### Why use OAuth-based authentication? + +OAuth-based authentication provides several key advantages for securing MCP +servers: + +- **Standard and interoperable:** You can connect ToolHive to any OAuth 2.1 or + OIDC-compliant IdP without custom code, supporting both human users and + automated services. +- **Proven and secure:** Authentication is delegated to battle-tested identity + systems, which handle login UI, multi-factor authentication, and password + storage. +- **Decoupled identity management:** You can use your existing SSO/IdP + infrastructure, making onboarding and management seamless. +- **Flexible for users and services:** This authentication framework supports + both interactive user login (for example, Google sign-in) and + service-to-service authentication (for example, Kubernetes service account + tokens). + +### Real-world authentication scenarios + +Understanding how OAuth-based authentication works in practice helps you design +better security for your MCP servers: + +**User login via Google:** For example, you can run an MCP server that requires +authentication using your Google credentials. ToolHive delegates login to +Google, receives an access token (either a JWT or an opaque token), and +validates it to authenticate you. This means users get a familiar login +experience while you benefit from Google's security infrastructure. + +**Service-to-service auth with Kubernetes:** If you run a microservice in a +Kubernetes cluster, it can present its service account token (typically an OIDC +JWT) to ToolHive. ToolHive validates the token using the cluster's OIDC issuer +and JWKS endpoint, enabling secure, automated authentication for your internal +services. + +### Token-based authentication + +ToolHive supports two types of access tokens for authentication: + +**JWT tokens (JSON Web Tokens):** Self-contained tokens that include identity +information within the token itself. JWTs are validated locally using +cryptographic signatures and consist of three parts: + +1. **Header:** Metadata about the token +2. **Payload:** Claims about the entity (typically you or your service) +3. **Signature:** Ensures the token hasn't been altered + +**Opaque tokens:** Reference tokens that don't contain identity information +directly. ToolHive validates these tokens by querying the identity provider's +token introspection endpoint to retrieve the associated claims. + +ToolHive automatically detects the token type and uses the appropriate +validation method—attempting JWT validation first and falling back to token +introspection if needed. + +### Authentication flow + +The authentication process follows these steps: + +1. **Token acquisition:** You obtain an access token from your identity + provider. +2. **Token presentation:** You include the token in your requests to ToolHive + (typically in the Authorization header). +3. **Token validation:** ToolHive validates the token using either: + - **Local validation:** For JWT tokens, verifying the signature, expiration, + and claims using the provider's public keys (JWKS) + - **Remote validation:** For opaque tokens, querying the provider's token + introspection endpoint to verify the token and retrieve claims +4. **Identity extraction:** ToolHive extracts your identity information from the + validated token claims. + +```mermaid +flowchart TD + Client -->|Access Token| ToolHive + ToolHive -->|Validate Token| Identity_Provider[Identity Provider] + ToolHive -->|Evaluate Cedar Policy| Cedar_Authorizer[Cedar Authorizer] + Cedar_Authorizer -->|Permit| MCP_Server + Cedar_Authorizer -->|Deny| Denied[403 Forbidden] +``` + +### Embedded authorization server + +In the standard authentication flow described above, clients obtain tokens +independently from an external identity provider and present them to ToolHive +for validation. The embedded authorization server provides an alternative model +where ToolHive itself acts as an OAuth authorization server, retrieving tokens +from an upstream identity provider on behalf of clients. + +:::note + +The embedded authorization server is currently available only for Kubernetes +deployments using the ToolHive Operator. + +::: + +From the client's perspective, the embedded authorization server provides a +standard OAuth 2.0 experience: + +1. If the client is not yet registered, it registers via Dynamic Client + Registration (DCR), receiving a `client_id` and `client_secret`. +2. The client is directed to the ToolHive authorization endpoint. +3. ToolHive redirects the client to the upstream identity provider for + authentication (for example, signing in with GitHub or Atlassian). +4. ToolHive exchanges the authorization code for upstream tokens and issues its + own JWT to the client, signed with keys you configure. +5. The client includes this JWT as a `Bearer` token in the `Authorization` + header on subsequent requests. + +Behind the scenes, ToolHive stores the upstream tokens and uses them to +authenticate MCP server requests to external APIs. For the complete flow, +including token storage and forwarding, see +[Embedded authorization server](./backend-auth.mdx#embedded-authorization-server). + +For Kubernetes setup instructions, see +[Set up embedded authorization server authentication](../guides-k8s/auth-k8s.mdx#set-up-embedded-authorization-server-authentication). + +### Identity providers + +ToolHive can integrate with any provider that supports OAuth 2.1 or OIDC, +including: + +- Google +- GitHub +- Microsoft Entra ID (Azure AD) +- Okta +- Auth0 +- Kubernetes (service account tokens) + +These same providers work with both external token validation and the embedded +authorization server. For the embedded authorization server, the upstream +provider must support the OAuth 2.0 authorization code flow. + +### Token validation methods + +ToolHive supports multiple token validation methods to work with different +identity providers: + +- **JWT validation:** For providers that issue JWT tokens, ToolHive validates + tokens locally using the provider's JWKS endpoint. This verifies the token's + signature, expiration, and claims without calling the identity provider for + each request. +- **Token introspection:** For providers that issue opaque tokens, ToolHive + validates tokens by querying the provider's introspection endpoint. This + supports RFC 7662 (OAuth 2.0 Token Introspection), Google's tokeninfo API, and + GitHub's token validation API. + +ToolHive automatically detects the token type—it first attempts JWT validation, +and if that fails, it falls back to token introspection. This means you don't +need to configure which validation method to use; ToolHive handles it +automatically based on the token format. + +## Authorization framework + +After authentication, ToolHive enforces authorization using Amazon's Cedar +policy language. ToolHive acts as a gateway in front of MCP servers, handling +all authorization checks before requests reach the server logic. This means MCP +servers do not need to implement their own OAuth or custom authorization +logic—ToolHive centralizes and standardizes access control. + +### Why Cedar for authorization? + +Cedar provides several advantages for MCP server authorization: + +- **Expressive and flexible:** Cedar supports both role-based (RBAC) and + attribute-based (ABAC) access control patterns, letting you create policies + that match your security requirements. +- **Formally verified:** Cedar's design has been formally verified for safety + and security properties, which reduces the risk of policy bugs. +- **Human-readable:** Cedar policies use clear, declarative syntax that's easy + to read, write, and audit. +- **Policy enforcement point:** ToolHive blocks unauthorized requests before + they reach the MCP server, which reduces risk and simplifies server code. +- **Secure by default:** Authorization is explicit—if a request is not + explicitly permitted, it is denied. Deny rules take precedence over permit + rules (deny overrides). + +### Authorization components + +ToolHive's authorization framework consists of: + +1. **Cedar authorizer:** Evaluates Cedar policies to determine if a request is + authorized +2. **Authorization middleware:** Extracts information from MCP requests and uses + the Cedar Authorizer +3. **Configuration:** A JSON or YAML file that specifies the Cedar policies and + entities + +### Authorization flow + +When a request arrives at an MCP server with authorization enabled: + +1. The authentication middleware authenticates the client and adds token claims + to the request context +2. The authorization middleware extracts information from the request + (principal, action, resource, and any arguments) +3. The Cedar authorizer evaluates policies to determine if the request is + authorized +4. If authorized, the request proceeds; otherwise, a 403 Forbidden response is + returned + +```mermaid +flowchart TD + Client -->|Access Token| ToolHive + ToolHive -->|Validate Token| Auth_Middleware + Auth_Middleware -->|Extract Claims| Authz_Middleware + Authz_Middleware -->|Evaluate Cedar Policies| Cedar_Authorizer + Cedar_Authorizer -->|Permit| MCP_Server + Cedar_Authorizer -->|Deny| Denied[403 Forbidden] +``` + +## Security and operational benefits + +ToolHive's authentication and authorization approach provides several key +benefits: + +- **Separation of concerns:** Authentication and authorization are handled + independently, following security best practices. +- **Integration with existing systems:** Use your existing identity + infrastructure (SSO, IdPs, Kubernetes, etc.). +- **Centralized, flexible policy model:** Define precise, auditable access rules + in a single place—no need to modify MCP server code. +- **Secure by default:** Requests are denied unless explicitly permitted by + policy, with deny precedence for maximum safety. +- **Auditable and versionable:** Policies are clear, declarative, and can be + tracked in version control for compliance and review. +- **Developer and operator friendly:** ToolHive acts as a smart proxy, so you + don't need to implement complex OAuth or custom auth logic in every server. + +## Client authentication support + +While ToolHive provides a robust authentication and authorization framework for +MCP servers, authentication support varies across the MCP client ecosystem. + +### MCP client capabilities + +The MCP ecosystem includes numerous clients with varying levels of +authentication support. Authentication support is not universal. Some clients +focus primarily on local, unauthenticated MCP servers for development workflows, +while others provide enterprise-grade authentication for production deployments. + +When selecting an MCP client for authenticated workflows, look for clients that +support the MCP authentication standards, including OAuth 2.1 and +transport-level authentication mechanisms. + +ToolHive's OIDC-based authentication approach aligns with industry standards and +works with clients that support modern authentication protocols. As the MCP +ecosystem continues to mature, we expect authentication support to become more +standardized across clients. + +## Related information + +- For configuring the embedded authorization server in Kubernetes, see + [Embedded authorization server authentication](../guides-k8s/auth-k8s.mdx#set-up-embedded-authorization-server-authentication) +- For backend authentication concepts, see + [Backend authentication](./backend-auth.mdx) +- For detailed policy writing guidance, see + [Cedar policies](./cedar-policies.mdx) diff --git a/versioned_docs/version-1.0/toolhive/concepts/backend-auth.mdx b/versioned_docs/version-1.0/toolhive/concepts/backend-auth.mdx new file mode 100644 index 00000000..04efb279 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/concepts/backend-auth.mdx @@ -0,0 +1,477 @@ +--- +title: Backend authentication +description: + Understanding how MCP servers authenticate to external services using + ToolHive's backend authentication patterns, including static credentials, + token exchange, and the embedded authorization server. +--- + +This document explains how ToolHive helps MCP servers authenticate to +third-party APIs and backend services exposed through the MCP servers. You'll +learn about the backend authentication patterns ToolHive supports, why they +improve security and multi-tenancy, and how they simplify MCP server +development. + +:::info[Scope of this documentation] + +This documentation covers **MCP-server-to-backend authentication**—how MCP +servers authenticate to external services or APIs they call (for example, a +GitHub MCP server authenticating to the GitHub API). + +This is different from **client-to-MCP-server authentication**, which involves +how clients authenticate to the MCP server itself. That topic is covered in +[Authentication and authorization](./auth-framework.mdx). + +::: + +## The challenge of backend authentication + +The MCP specification focuses on authorization to the MCP server but doesn't +specify how an MCP server should authenticate to the services it exposes. This +is intentionally left to implementers, which makes sense from a specification +perspective but leaves MCP server developers without clear guidance. + +Many MCP servers today either embed static API keys or require custom +authentication code. This creates several problems: + +- **Security risks:** Long-lived credentials stored in configuration files or + environment variables can be compromised +- **Audit challenges:** When multiple users share a service account, you can't + trace individual actions +- **Multi-tenancy complexity:** Supporting multiple tenants with isolated + credentials requires significant custom code + +ToolHive addresses these challenges with three backend authentication patterns: +static credentials for services that don't support OAuth, token exchange for +services in the same or federated trust domain, and the embedded authorization +server for OAuth-based external APIs where no federation exists. + +## How ToolHive handles backend authentication + +ToolHive sits between clients and MCP servers, and can acquire backend +credentials on behalf of the MCP server. Depending on the pattern, it might +exchange the client's token, run an OAuth flow against an external provider, or +inject static credentials. In each case, the MCP server receives ready-to-use +credentials—via an `Authorization: Bearer` header, another header, or +environment variables, depending on the pattern—without needing to implement +custom authentication logic or manage secrets directly. + +## Backend authentication patterns + +ToolHive supports three patterns for backend authentication. Which one you use +depends on the relationship between your identity provider (IdP) and the backend +service. + +### Static credentials and API keys + +When a backend service requires API keys, database passwords, or other static +credentials, you can configure them directly in ToolHive as environment +variables, secret files, or injected headers. + +This is the simplest pattern, but it provides the least security and +auditability. Static credentials are long-lived and shared across all users, so +there is no per-user attribution in audit logs. + +When using static credentials, consider integrating with a secret management +system like [HashiCorp Vault](../integrations/vault.mdx) for secure storage and +rotation. + +### Token exchange + +When the backend service trusts the same IdP as your MCP clients—or federation +is configured between the two IdPs—ToolHive can exchange the client's token for +one scoped to the backend service using RFC 8693 token exchange. This preserves +the user's identity across services and provides short-lived, narrowly scoped +tokens. Because the trust relationship is pre-configured at the IdP, the +exchange is transparent to the end user—no consent screen required. + +#### Same IdP with token exchange + +When both the MCP server and the backend service trust the same IdP, and that +IdP supports [RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) token +exchange, ToolHive can exchange the internal token for an external one. + +```mermaid +flowchart LR + User[User] + IDP[Identity Provider] + ToolHive[ToolHive Middleware] + MCP[MCP Server] + Upstream[Upstream Service] + + User -->|login & receive token| IDP + User -->|request with token| ToolHive + ToolHive -->|subject_token → exchange| IDP + IDP -->|external_token issued| ToolHive + ToolHive -->|pass external_token| MCP + MCP -->|calls with external_token| Upstream + Upstream -->|validates token| IDP +``` + +**How it works:** + +1. The user authenticates to the MCP client and receives an access token from + the IdP +2. ToolHive's token exchange middleware contacts the IdP, presenting the user's + access token +3. The IdP issues a new access token with different audience and scopes +4. ToolHive passes this access token to the MCP server +5. The MCP server uses the access token to call the upstream service + +#### Federated IdPs with identity mapping + +When the backend service trusts a different IdP, but federation is configured +between the two IdPs, ToolHive can use the federated identity service to issue +short-lived tokens. Examples include Google's Security Token Service (STS) for +Google Cloud services and AWS STS for AWS services—both can issue tokens based +on your corporate identity. + +```mermaid +flowchart LR + User[User] + IDP1[IdP A - User Login] + ToolHive[ToolHive Middleware] + IDP2[IdP B - Federated] + MCP[MCP Server] + Upstream[Upstream Service] + + User -->|login & receive token_A| IDP1 + User -->|request with token_A| ToolHive + ToolHive -->|submit token_A| IDP2 + IDP2 -->|issue token_B| ToolHive + ToolHive -->|pass token_B| MCP + MCP -->|call with token_B| Upstream + Upstream -->|validate token_B| IDP2 +``` + +**How it works:** + +1. The user authenticates to their MCP client with a corporate IdP and receives + token_A +2. ToolHive submits token_A to the federated identity service +3. The federated service maps the identity and issues token_B +4. ToolHive passes token_B to the MCP server +5. The MCP server uses token_B to call the upstream service + +### Embedded authorization server + +When the MCP server needs to call an external API where no federation +relationship exists—such as GitHub, Google Workspace, or Atlassian APIs—the +embedded authorization server handles the full OAuth web flow against the +external provider. The proxy redirects the user to authenticate directly with +the external service, obtains tokens on behalf of the user, and passes the +upstream token to the MCP server. + +```mermaid +sequenceDiagram + participant User + participant Proxy as ToolHive Proxy + participant ExtProvider as External Provider + + User->>Proxy: Connect + Proxy-->>User: Redirect to login + User->>ExtProvider: Authenticate + ExtProvider->>Proxy: Authorization code + Proxy->>ExtProvider: Exchange code for token + ExtProvider->>Proxy: Upstream tokens + Proxy->>User: Issue JWT +``` + +On subsequent MCP requests, ToolHive uses the JWT to retrieve the stored +upstream tokens and forward them to the MCP server. For details on this +mechanism, see [Token storage and forwarding](#token-storage-and-forwarding). + +The embedded authorization server runs in-process within the ToolHive proxy—no +separate infrastructure is needed. It supports Dynamic Client Registration +(DCR), so MCP clients can register automatically with ToolHive—no manual client +configuration in ToolHive is required. + +:::note + +The embedded authorization server is currently available only for Kubernetes +deployments using the ToolHive Operator. + +::: + +#### Key characteristics + +- **In-process execution:** The authorization server runs within the ToolHive + proxy—no separate infrastructure or sidecar containers needed. +- **Configurable signing keys:** JWTs are signed with keys you provide, + supporting key rotation for zero-downtime updates. +- **Flexible upstream providers:** Supports both OIDC providers (with automatic + endpoint discovery) and OAuth 2.0 providers (with explicit endpoint + configuration). +- **Configurable token lifespans:** Access tokens, refresh tokens, and + authorization codes have configurable durations with sensible defaults. +- **Dynamic Client Registration (DCR):** Supports OAuth 2.0 Dynamic Client + Registration (RFC 7591), allowing MCP clients to register automatically with + ToolHive's authorization server—no manual client registration in ToolHive is + required. +- **Direct upstream redirect:** The embedded authorization server redirects + clients directly to the upstream provider for authentication (for example, + GitHub or Atlassian). +- **Single upstream provider:** Currently supports one upstream identity + provider per configuration. + +:::info[Chained authentication not yet supported] + +The embedded authorization server redirects clients directly to the upstream +provider. This means the upstream provider must be the service whose API the MCP +server calls. Chained authentication—where a client authenticates with a +corporate IdP like Okta, which then federates to an external provider like +GitHub—is not yet supported. If your deployment requires this pattern, consider +using [token exchange](#same-idp-with-token-exchange) with a federated identity +provider instead. + +::: + +#### Token storage and forwarding + +The embedded authorization server stores upstream tokens (access tokens, refresh +tokens, and ID tokens from external providers) in session storage. When the +OAuth flow completes, the server generates a unique session ID and stores the +upstream tokens keyed by this ID. The JWT issued to the client contains a `tsid` +(Token Session ID) claim that references this session. + +When a client makes an MCP request with this JWT: + +1. The ToolHive proxy validates the JWT signature and extracts the `tsid` claim +2. It retrieves the upstream tokens from session storage using the `tsid` +3. The proxy replaces the `Authorization` header with the upstream access token +4. The request is forwarded to the MCP server with the external provider's token + +```mermaid +sequenceDiagram + participant Client + participant Proxy as ToolHive Proxy + participant Store as Session Storage + participant MCP as MCP Server + participant API as External API + + Note over Client,Store: Initial OAuth flow + Proxy->>Store: Store upstream tokens
keyed by session ID + Proxy-->>Client: Issue JWT with tsid claim + + Note over Client,API: Subsequent MCP requests + Client->>Proxy: MCP request with JWT + Proxy->>Proxy: Validate JWT signature + Proxy->>Store: Look up upstream token
using tsid from JWT + Store-->>Proxy: Return upstream access token + Proxy->>MCP: Forward request with
upstream access token + MCP->>API: Call external API + API-->>MCP: Response + MCP-->>Proxy: Response + Proxy-->>Client: Response +``` + +This mechanism allows MCP servers to call external APIs with the user's actual +credentials from the upstream provider, while the client only needs to manage a +single ToolHive-issued JWT. + +#### Automatic token refresh + +Upstream access tokens have their own expiration, independent of the ToolHive +JWT lifespan. When the stored upstream access token has expired, ToolHive +automatically refreshes it using the stored refresh token before forwarding the +request — your MCP session continues without re-authentication. + +If the refresh token is also expired or has been revoked by the upstream +provider, ToolHive returns a `401` response, prompting you to re-authenticate +through the OAuth flow. + +:::warning[Session storage limitations] + +By default, session storage is in-memory only. Upstream tokens are lost when +pods restart, requiring users to re-authenticate. For production deployments, +configure Redis Sentinel as the storage backend for persistent, highly available +session storage. See +[Configure session storage](../guides-k8s/auth-k8s.mdx#configure-session-storage) +for a quick setup, or the full +[Redis Sentinel session storage](../guides-k8s/redis-session-storage.mdx) +tutorial for an end-to-end walkthrough. + +::: + +For the client-facing OAuth flow, see +[Embedded authorization server](./auth-framework.mdx#embedded-authorization-server). +For Kubernetes setup instructions, see +[Set up embedded authorization server authentication](../guides-k8s/auth-k8s.mdx#set-up-embedded-authorization-server-authentication). + +## Token exchange in depth + +This section provides implementation details for the token exchange patterns +described above. For setup instructions, see +[Configure token exchange](../guides-cli/token-exchange.mdx) (CLI) or +[Configure token exchange in Kubernetes](../guides-k8s/token-exchange-k8s.mdx). + +### Same IdP with token exchange + +The token exchange flow demonstrates how ToolHive transforms user identity +tokens into properly scoped service tokens. + +```mermaid +sequenceDiagram + participant Client + participant ToolHive + participant IDP + participant MCP Server + participant Upstream Service + + Client->>ToolHive: Request with user token + ToolHive->>IDP: Validate token + IDP-->>ToolHive: Token valid + ToolHive->>IDP: Exchange token request + IDP-->>ToolHive: Service token + ToolHive->>MCP Server: Request with service token + MCP Server->>Upstream Service: API call + Upstream Service-->>MCP Server: Response + MCP Server-->>ToolHive: Response + ToolHive-->>Client: Response +``` + +When a client authenticates to ToolHive, it receives a token scoped for the MCP +server: + +```json +{ + "iss": "https://idp.example.com/oauth2/default", + "aud": "mcp-server", + "scp": ["backend-mcp:tools:call", "backend-mcp:tools:list"], + "sub": "user@example.com" +} +``` + +ToolHive's token exchange middleware contacts the IdP and exchanges this token +for one scoped to the backend service: + +```json +{ + "iss": "https://idp.example.com/oauth2/default", + "aud": "backend-server", + "scp": ["backend-api:read"], + "sub": "user@example.com" +} +``` + +Notice how the audience (`aud`) and scopes (`scp`) change while preserving the +user's identity (`sub`). This exchanged token is then injected into the +`Authorization: Bearer` HTTP header and passed to the MCP server. + +### Federated IdPs with identity mapping + +When using federated identity providers, ToolHive can map your corporate +identity to an external service identity. This is particularly useful for +accessing cloud services like Google Cloud Platform. + +The client authenticates with your corporate IdP and receives a token: + +```json +{ + "iss": "https://idp.example.com/oauth2/default", + "aud": "mcp-server", + "sub": "user@example.com", + "email": "user@example.com", + "scp": ["mcp:tools:call", "mcp:tools:list"], + "exp": 1729641600, + "iat": 1729638000 +} +``` + +ToolHive's token exchange middleware calls the external Security Token Service +(STS) endpoint. For Google Cloud, this looks like: + +```http +POST https://sts.googleapis.com/v1/token +Content-Type: application/x-www-form-urlencoded + +grant_type=urn:ietf:params:oauth:grant-type:token-exchange +&audience=//iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID +&scope=https://www.googleapis.com/auth/bigquery +&requested_token_type=urn:ietf:params:oauth:token-type:access_token +&subject_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9... +&subject_token_type=urn:ietf:params:oauth:token-type:jwt +``` + +The federated service returns a token that maps your corporate identity to a +federated principal: + +```json +{ + "iss": "https://sts.googleapis.com", + "sub": "principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/subject/user@example.com", + "aud": "https://bigquery.googleapis.com/", + "email": "user@example.com", + "scope": "https://www.googleapis.com/auth/bigquery", + "exp": 1729641600, + "iat": 1729638000 +} +``` + +This exchanged token is injected into the `Authorization: Bearer` HTTP header +and passed to the MCP server. The MCP server uses this token to make upstream +API calls, with each request attributed to the individual user's federated +identity rather than a shared service account. + +Federation-based token exchange has several important characteristics that +distinguish it from standard token exchange: + +1. **No client authentication required:** The external STS endpoint doesn't + require `client_id` or `client_secret`. The OAuth JWT itself serves as proof + of identity. +2. **Identity federation pool as intermediary:** The `audience` parameter points + to a federation pool configuration, not directly to the target service. +3. **Principal mapping:** User attributes (email, sub) from the OAuth token are + mapped to federated principals for access control. +4. **Individual audit trail:** Upstream service audit logs show the individual + user identity, not a service account. + +## Security and operational benefits + +ToolHive's token-based authentication patterns (token exchange and the embedded +authorization server) provide several key advantages over static credentials: + +- **Secure:** MCP servers receive short-lived, properly scoped access tokens + instead of embedding long-lived secrets +- **Auditable:** Each API call is attributed to the individual user identity, + making audit trails clear and meaningful +- **Multi-tenant friendly:** Token scoping naturally supports tenant isolation + and separation of duties +- **Developer friendly:** MCP servers don't need custom authentication + logic—they just use the provided token +- **Least privilege:** Tokens are narrowly scoped to specific audiences and + permissions, reducing the blast radius if compromised +- **Consistent:** The same pattern works across different backend services and + identity providers + +## Choosing the right backend authentication pattern + +| Scenario | Pattern | Why | +| ------------------------------------------------------------------- | -------------------------------------------------------------------- | ----------------------------------------------------------------- | +| Backend only accepts API keys or static credentials | [Static credentials](#static-credentials-and-api-keys) | No OAuth support; configure credentials directly in ToolHive | +| Backend trusts the same IdP as your clients | [Token exchange (same IdP)](#same-idp-with-token-exchange) | Exchange the client token for a backend-scoped token via RFC 8693 | +| Backend trusts a federated IdP (for example, Google Cloud) | [Token exchange (federation)](#federated-idps-with-identity-mapping) | Map your corporate identity to the federated service | +| Backend is an external API with no federation (for example, GitHub) | [Embedded authorization server](#embedded-authorization-server) | Run the full OAuth flow against the external provider | + +### Built-in AWS STS support + +For AWS services like the +[AWS MCP Server](https://docs.aws.amazon.com/aws-mcp/), ToolHive has built-in +support for exchanging OIDC tokens for temporary AWS credentials using +`AssumeRoleWithWebIdentity`. This handles the STS exchange and SigV4 request +signing automatically, with claim-based IAM role selection. See the +[AWS STS integration tutorial](../integrations/aws-sts.mdx) for a step-by-step +setup guide. + +## Related information + +- For client authentication concepts, see + [Authentication and authorization](./auth-framework.mdx) +- For the embedded authorization server, see + [Embedded authorization server](./auth-framework.mdx#embedded-authorization-server) +- For configuring the embedded authorization server in Kubernetes, see + [Set up embedded authorization server authentication](../guides-k8s/auth-k8s.mdx#set-up-embedded-authorization-server-authentication) +- For configuring token exchange, see + [Configure token exchange](../guides-cli/token-exchange.mdx) (CLI) or + [Configure token exchange in Kubernetes](../guides-k8s/token-exchange-k8s.mdx) +- For policy configuration, see [Cedar policies](./cedar-policies.mdx) diff --git a/versioned_docs/version-1.0/toolhive/concepts/cedar-policies.mdx b/versioned_docs/version-1.0/toolhive/concepts/cedar-policies.mdx new file mode 100644 index 00000000..ec598b6e --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/concepts/cedar-policies.mdx @@ -0,0 +1,408 @@ +--- +title: Cedar policies +description: Writing and configuring Cedar policies for MCP server authorization. +--- + +This document provides detailed guidance on writing and configuring Cedar +policies for MCP server authorization. You'll learn how to create effective +policies, configure authorization settings, and troubleshoot common issues. + +:::info + +For the conceptual overview of authentication and authorization, see +[Authentication and authorization framework](./auth-framework.mdx). + +::: + +## Cedar policy language + +Cedar policies express authorization rules in a clear, declarative syntax: + +```text +permit|forbid(principal, action, resource) when { conditions }; +``` + +- `permit` or `forbid`: Whether to allow or deny the operation +- `principal`: The entity making the request (the client) +- `action`: The operation being performed +- `resource`: The object being accessed +- `conditions`: Optional conditions that must be satisfied + +## MCP-specific entities + +In the context of MCP servers, Cedar policies use the following entities: + +### Principal + +The client making the request, identified by the `sub` claim in the access +token: + +- Format: `Client::` +- Example: `Client::user123` + +### Action + +The operation being performed on an MCP feature: + +- Format: `Action::` +- Examples: + - `Action::"call_tool"`: Call a tool + - `Action::"get_prompt"`: Get a prompt + - `Action::"read_resource"`: Read a resource + - `Action::"list_tools"`: List available tools + +### Resource + +The object being accessed: + +- Format: `::` +- Examples: + - `Tool::"weather"`: The weather tool + - `Prompt::"greeting"`: The greeting prompt + - `Resource::"data"`: The data resource + +## Configuration formats + +You can configure Cedar authorization using either JSON or YAML format: + +### JSON configuration + +```json +{ + "version": "1.0", + "type": "cedarv1", + "cedar": { + "policies": [ + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"weather\");", + "permit(principal, action == Action::\"get_prompt\", resource == Prompt::\"greeting\");", + "permit(principal, action == Action::\"read_resource\", resource == Resource::\"data\");" + ], + "entities_json": "[]" + } +} +``` + +### YAML configuration + +```yaml +version: '1.0' +type: cedarv1 +cedar: + policies: + - 'permit(principal, action == Action::"call_tool", resource == + Tool::"weather");' + - 'permit(principal, action == Action::"get_prompt", resource == + Prompt::"greeting");' + - 'permit(principal, action == Action::"read_resource", resource == + Resource::"data");' + entities_json: '[]' +``` + +### Configuration fields + +- `version`: The version of the configuration format +- `type`: The type of authorization configuration (currently only `cedarv1` is + supported) +- `cedar`: The Cedar-specific configuration + - `policies`: An array of Cedar policy strings + - `entities_json`: A JSON string representing Cedar entities + +## Writing effective policies + +Understanding how to write Cedar policies is crucial for securing your MCP +servers effectively. This section provides practical guidance for creating +policies that match your security requirements. + +### Basic policy patterns + +Start with simple policies and build complexity as needed: + +#### Allow specific tool access + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"weather"); +``` + +This policy allows any authenticated client to call the weather tool. It's +useful when you want to provide broad access to specific functionality. + +#### Allow specific user access + +```text +permit(principal == Client::"user123", action == Action::"call_tool", resource); +``` + +This policy allows a specific user to call any tool. Use this pattern when you +need to grant broad permissions to trusted users. + +### Role-based access control (RBAC) + +RBAC policies use roles from JWT claims to determine access: + +```text +permit(principal, action == Action::"call_tool", resource) when { + principal.claim_roles.contains("admin") +}; +``` + +This policy allows clients with the "admin" role to call any tool. RBAC is +effective when you have well-defined roles in your organization. + +### Attribute-based access control (ABAC) + +ABAC policies use multiple attributes to make fine-grained decisions: + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"sensitive_data") when { + principal.claim_roles.contains("data_analyst") && + resource.arg_data_level <= principal.claim_clearance_level +}; +``` + +This policy allows data analysts to access sensitive data, but only if their +clearance level is sufficient. ABAC provides the most flexibility for complex +security requirements. + +## Working with JWT claims + +JWT claims from your identity provider become available in policies with a +`claim_` prefix. You can use these claims in two ways: + +**On the principal entity:** + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"weather") when { + principal.claim_name == "John Doe" +}; +``` + +**In the context:** + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"weather") when { + context.claim_name == "John Doe" +}; +``` + +Both approaches work identically. Choose the one that makes your policies more +readable. + +## Working with tool arguments + +Tool arguments become available in policies with an `arg_` prefix. This lets you +create policies based on the specific parameters of requests: + +**On the resource entity:** + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"weather") when { + resource.arg_location == "New York" || resource.arg_location == "London" +}; +``` + +**In the context:** + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"weather") when { + context.arg_location == "New York" || context.arg_location == "London" +}; +``` + +This policy allows weather tool calls only for specific locations, demonstrating +how you can control access based on request parameters. + +## List operations and filtering + +List operations (`tools/list`, `prompts/list`, `resources/list`) work +differently from other operations. They're always allowed, but the response is +automatically filtered based on what the user can actually access: + +- `tools/list` shows only tools the user can call (based on `call_tool` + policies) +- `prompts/list` shows only prompts the user can get (based on `get_prompt` + policies) +- `resources/list` shows only resources the user can read (based on + `read_resource` policies) + +You don't need to write explicit policies for list operations. Instead, focus on +the underlying access policies, and the lists will be filtered automatically. + +For example, if you have this policy: + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"weather"); +``` + +Then `tools/list` will only show the "weather" tool for that user. + +## Policy evaluation and secure defaults + +Understanding how Cedar evaluates policies helps you write more effective and +secure authorization rules. + +### Evaluation order + +ToolHive's policy evaluation follows a secure-by-default, least-privilege model: + +1. **Deny precedence:** If any `forbid` policy matches, the request is denied +2. **Permit evaluation:** If any `permit` policy matches, the request is + authorized +3. **Default deny:** If no policy matches, the request is denied + +This means that `forbid` policies always override `permit` policies, and any +request not explicitly permitted is denied. This approach minimizes risk and +ensures that only authorized actions are allowed. + +### Designing secure policies + +When writing policies, follow these principles: + +**Start with least privilege:** Begin by denying everything, then add specific +permissions as needed. This approach is more secure than starting with broad +permissions and then trying to restrict them. + +**Use explicit deny sparingly:** While `forbid` policies can be useful, they can +also make your policy set harder to understand. In most cases, the default deny +behavior is sufficient. + +**Test your policies:** Always test policies with real requests to ensure they +work as expected. Pay special attention to edge cases and error conditions. + +## Advanced policy examples + +### Combining JWT claims and tool arguments + +You can combine JWT claims and tool arguments in your policies to create more +sophisticated authorization rules: + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"sensitive_data") when { + principal.claim_roles.contains("data_analyst") && + resource.arg_data_level <= principal.claim_clearance_level +}; +``` + +This policy allows clients with the "data_analyst" role to access the +sensitive_data tool, but only if their clearance level (from JWT claims) is +sufficient for the requested data level (from tool arguments). + +### Multi-tenant environments + +In multi-tenant environments, you can use policies to isolate tenants: + +```text +permit(principal, action, resource) when { + principal.claim_tenant_id == resource.tenant_id +}; +``` + +This ensures that clients can only access resources belonging to their tenant. + +### Data sensitivity levels + +For data with different sensitivity levels: + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"data_access") when { + principal.claim_clearance_level >= resource.arg_data_sensitivity +}; +``` + +This ensures that clients can only access data within their clearance level. + +### Geographic restrictions + +For geographically restricted resources: + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"geo_restricted") when { + principal.claim_location in ["US", "Canada", "Mexico"] +}; +``` + +This restricts access based on the client's location. + +### Time-based access + +For resources that should only be accessible during certain hours: + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"business_hours") when { + context.current_hour >= 9 && context.current_hour <= 17 +}; +``` + +This restricts access to business hours only. + +## Entity attributes + +Cedar entities can have attributes that can be used in policy conditions. The +authorization middleware automatically adds JWT claims and tool arguments as +attributes to the principal entity. + +You can also define custom entities with attributes in the `entities_json` field +of the configuration file: + +```json +{ + "version": "1.0", + "type": "cedarv1", + "cedar": { + "policies": [ + "permit(principal, action == Action::\"call_tool\", resource) when { resource.owner == principal.claim_sub };" + ], + "entities_json": "[ + { + \"uid\": \"Tool::weather\", + \"attrs\": { + \"owner\": \"user123\" + } + } + ]" + } +} +``` + +This configuration defines a custom entity for the weather tool with an `owner` +attribute set to `user123`. The policy allows clients to call tools only if they +own them. + +## Troubleshooting policies + +When policies don't work as expected, follow this systematic approach: + +### Request is denied unexpectedly + +1. **Check policy syntax:** Ensure your policies are correctly formatted and use + valid Cedar syntax. +2. **Verify entity matching:** Confirm that the principal, action, and resource + in your policies match the actual values in the request. +3. **Test conditions:** Check that any conditions in your policies are satisfied + by the request context. +4. **Remember default deny:** If no policy explicitly permits the request, it + will be denied. + +### JWT claims are not available + +1. **Verify JWT middleware:** Ensure that JWT authentication is configured + correctly and running before authorization. +2. **Check token claims:** Verify that the JWT token contains the expected + claims. +3. **Use correct prefix:** Remember that JWT claims are available with a + `claim_` prefix. + +### Tool arguments are not available + +1. **Check request format:** Ensure that tool arguments are correctly specified + in the request. +2. **Use correct prefix:** Remember that tool arguments are available with an + `arg_` prefix. +3. **Verify argument names:** Confirm that the argument names in your policies + match those in the actual requests. + +## Related information + +- For the conceptual overview, see + [Authentication and authorization framework](./auth-framework.mdx) +- For detailed Cedar policy syntax, see + [Cedar documentation](https://docs.cedarpolicy.com/) diff --git a/versioned_docs/version-1.0/toolhive/concepts/embedded-auth-server.mdx b/versioned_docs/version-1.0/toolhive/concepts/embedded-auth-server.mdx new file mode 100644 index 00000000..109bb07d --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/concepts/embedded-auth-server.mdx @@ -0,0 +1,193 @@ +--- +title: Embedded authorization server +description: + How the ToolHive embedded authorization server works, including the OAuth + flow, token storage and forwarding, and when to use it. +--- + +The embedded authorization server is an OAuth 2.0 authorization server that runs +in-process within the ToolHive proxy. It solves a specific problem: how to +authenticate MCP server requests to external APIs—like GitHub, Google Workspace, +or Atlassian—where no federation relationship exists between your identity +provider and that external service. + +Without the embedded auth server, every MCP client would need to register its +own OAuth application with each external provider, manage redirect URIs, and +handle token acquisition separately. The embedded auth server centralizes this: +it handles the full OAuth web flow against the external provider on behalf of +clients, stores the resulting tokens, and issues its own JWTs that clients use +for subsequent requests. + +:::note + +The embedded authorization server is currently available only for Kubernetes +deployments using the ToolHive Operator. + +::: + +## When to use the embedded authorization server + +Use the embedded authorization server when your MCP servers call external APIs +on behalf of individual users and no federation relationship exists between your +identity provider and those services. + +| Scenario | Pattern to use | +| ------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | +| Backend only accepts API keys or static credentials | [Static credentials](./backend-auth.mdx#static-credentials-and-api-keys) | +| Backend trusts the same IdP as your clients | [Token exchange (same IdP)](./backend-auth.mdx#same-idp-with-token-exchange) | +| Backend trusts a federated IdP (for example, Google Cloud, AWS) | [Token exchange (federation)](./backend-auth.mdx#federated-idps-with-identity-mapping) | +| Backend is an external API with no federation (for example, GitHub) | **Embedded authorization server** (this page) | + +## How the OAuth flow works + +From the client's perspective, the embedded authorization server provides a +standard OAuth 2.0 experience: + +1. If the client is not yet registered, it registers via Dynamic Client + Registration (DCR, RFC 7591), receiving a `client_id` and `client_secret`. No + manual client registration in ToolHive is required. +2. The client is directed to the ToolHive authorization endpoint. +3. ToolHive redirects the client to the upstream identity provider for + authentication (for example, signing in with GitHub or Atlassian). +4. ToolHive exchanges the authorization code for upstream tokens and issues its + own JWT to the client, signed with keys you configure. +5. The client includes this JWT as a `Bearer` token in the `Authorization` + header on subsequent requests. + +```mermaid +sequenceDiagram + participant User + participant Proxy as ToolHive Proxy + participant ExtProvider as External Provider + + User->>Proxy: Connect + Proxy-->>User: Redirect to login + User->>ExtProvider: Authenticate + ExtProvider->>Proxy: Authorization code + Proxy->>ExtProvider: Exchange code for token + ExtProvider->>Proxy: Upstream tokens + Proxy->>User: Issue JWT +``` + +Behind the scenes, ToolHive stores the upstream tokens in session storage and +uses them to authenticate MCP server requests to external APIs. The client only +manages a single ToolHive-issued JWT. + +## Token storage and forwarding + +When the OAuth flow completes, the embedded auth server generates a unique +session ID and stores the upstream tokens (access token, refresh token, and ID +token from the external provider) keyed by this ID in session storage. The JWT +issued to the client contains a `tsid` (Token Session ID) claim that references +this session. + +When a client makes an MCP request with this JWT: + +1. The ToolHive proxy validates the JWT signature and extracts the `tsid` claim. +2. It retrieves the upstream tokens from session storage using the `tsid`. +3. The proxy replaces the `Authorization` header with the upstream access token. +4. The request is forwarded to the MCP server with the external provider's + token. + +```mermaid +sequenceDiagram + participant Client + participant Proxy as ToolHive Proxy + participant Store as Session Storage + participant MCP as MCP Server + participant API as External API + + Note over Client,Store: Initial OAuth flow + Proxy->>Store: Store upstream tokens
keyed by session ID + Proxy-->>Client: Issue JWT with tsid claim + + Note over Client,API: Subsequent MCP requests + Client->>Proxy: MCP request with JWT + Proxy->>Proxy: Validate JWT signature + Proxy->>Store: Look up upstream token
using tsid from JWT + Store-->>Proxy: Return upstream access token + Proxy->>MCP: Forward request with
upstream access token + MCP->>API: Call external API + API-->>MCP: Response + MCP-->>Proxy: Response + Proxy-->>Client: Response +``` + +MCP servers receive the upstream access token in the `Authorization: Bearer` +header—they don't need to implement custom authentication logic or manage +secrets. + +## Automatic token refresh + +Upstream access tokens expire independently of the ToolHive JWT lifespan. When +the stored upstream access token has expired, ToolHive automatically refreshes +it using the stored refresh token before forwarding the request. Your MCP +session continues without re-authentication. + +If the refresh token is also expired or has been revoked by the upstream +provider, ToolHive returns a `401` response, prompting re-authentication through +the OAuth flow. + +## Key characteristics + +- **In-process execution:** The authorization server runs within the ToolHive + proxy—no separate infrastructure or sidecar containers needed. +- **Dynamic Client Registration (DCR):** Supports OAuth 2.0 DCR (RFC 7591), + allowing MCP clients to register automatically. No manual client registration + in ToolHive is required. +- **Direct upstream redirect:** Redirects clients directly to the upstream + provider for authentication (for example, GitHub or Atlassian). +- **Configurable signing keys:** JWTs are signed with keys you provide, + supporting key rotation for zero-downtime updates. +- **Flexible upstream providers:** Supports OIDC providers (with automatic + endpoint discovery) and plain OAuth 2.0 providers (with explicit endpoint + configuration). +- **Configurable token lifespans:** Access tokens, refresh tokens, and + authorization codes have configurable durations with sensible defaults. + +## Session storage + +By default, session storage is in-memory. Upstream tokens are lost when pods +restart, requiring users to re-authenticate. + +For production deployments, configure Redis Sentinel as the storage backend for +persistent, highly available session storage. See +[Configure session storage](../guides-k8s/auth-k8s.mdx#configure-session-storage) +for a quick setup, or the full +[Redis Sentinel session storage](../guides-k8s/redis-session-storage.mdx) guide +for an end-to-end walkthrough. + +## MCPServer vs. VirtualMCPServer + +The embedded auth server is available on both `MCPServer` and `VirtualMCPServer` +resources, with some differences: + +| | MCPServer | VirtualMCPServer | +| ---------------------- | ------------------------------------------- | ------------------------------------------------------------------------------ | +| Configuration location | Separate `MCPExternalAuthConfig` resource | Inline `authServerConfig` block on the resource | +| Upstream providers | Single upstream provider | Multiple upstream providers with sequential authorization chaining | +| Token forwarding | Automatic (single provider, single backend) | Explicit `upstreamInject` or `tokenExchange` config maps providers to backends | + +For single-backend deployments on MCPServer, the embedded auth server +automatically swaps the token for each request. For vMCP with multiple backends, +you configure which upstream provider's token goes to which backend using +[upstream token injection](../guides-vmcp/authentication.mdx#upstream-token-injection) +or +[token exchange with upstream tokens](../guides-vmcp/authentication.mdx#token-exchange-with-upstream-tokens). + +## Next steps + +- [Set up embedded authorization server authentication](../guides-k8s/auth-k8s.mdx#set-up-embedded-authorization-server-authentication) + — step-by-step setup for MCPServer resources in Kubernetes +- [vMCP embedded authorization server](../guides-vmcp/authentication.mdx#embedded-authorization-server) + — configuring multiple upstream providers on a VirtualMCPServer +- [Redis Sentinel session storage](../guides-k8s/redis-session-storage.mdx) — + production session storage configuration + +## Related information + +- [Authentication and authorization](./auth-framework.mdx) — client-to-MCP + authentication concepts and the overall framework +- [Backend authentication](./backend-auth.mdx) — all backend authentication + patterns, including when to choose the embedded auth server +- [Cedar policies](./cedar-policies.mdx) — authorization policy configuration diff --git a/versioned_docs/version-1.0/toolhive/concepts/groups.mdx b/versioned_docs/version-1.0/toolhive/concepts/groups.mdx new file mode 100644 index 00000000..1d10e784 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/concepts/groups.mdx @@ -0,0 +1,161 @@ +--- +title: Organizing MCP servers with groups +sidebar_label: Organizing MCP servers +description: + Understanding when and why to use groups for organizing MCP servers and + controlling client access. +--- + +As your MCP server usage grows, managing multiple servers becomes increasingly +complex. Groups provide a way to organize servers logically and control which +tools are available to different clients. + +## The problem groups solve + +Without groups, all your MCP servers live in a single pool. This creates +challenges: + +- **Tool overload**: AI clients see every tool from every server, making it + harder to select the right one +- **No separation of concerns**: Development tools mix with production tools +- **One-size-fits-all access**: Every client gets access to everything, even + when they only need a subset + +Groups address these issues by letting you organize servers into logical +collections and control which clients can access each collection. + +## When to use groups + +Groups are most valuable when you need to: + +- **Separate environments**: Keep development, staging, and production servers + isolated so you don't accidentally use production tools during development +- **Organize by project**: Give each team access to the tools they need without + cluttering their workspace with irrelevant servers +- **Customize client access**: Configure different AI clients with different + tool sets based on their purpose +- **Reduce context for AI**: Limit the tools available to an AI client so it can + make better decisions about which tool to use + +### When groups aren't necessary + +Groups add organizational overhead, so they're not always the right choice: + +- **Single server setups**: If you're running just one or two MCP servers, + groups don't provide much benefit +- **Universal access needs**: If all clients need access to all servers, a + single default group works fine +- **Simple personal use**: For individual developers with straightforward needs, + the default group is often sufficient + +## Organizational patterns + +Here are common ways teams organize their groups: + +### Environment-based groups + +Separate servers by deployment environment to prevent mixing development and +production contexts: + +| Group | Purpose | +| ----------- | ---------------------------------------------- | +| development | Experimental tools, local databases, test APIs | +| staging | Pre-production testing with realistic data | +| production | Live systems with appropriate access controls | + +This pattern is useful when: + +- You run the same MCP server with different configurations per environment +- You want to prevent accidental production access during development +- Different team members need different environment access + +This pattern works well when: + +- Teams have distinct tool requirements +- You want to reduce clutter in each team's AI client +- Different teams work on different parts of your system + +### Project-based groups + +Create groups for specific projects or products: + +| Group | Servers | +| -------------- | ------------------------------------------- | +| mobile-app | Firebase, app store tools, mobile analytics | +| web-platform | CMS tools, CDN management, web analytics | +| internal-tools | Admin dashboards, reporting tools | + +This approach helps when: + +- Projects have unique tool requirements +- You want focused AI assistance for specific work contexts +- Multiple teams contribute to the same project + +## Groups and client access + +One of the most powerful aspects of groups is controlling which AI clients can +access which servers. When you configure a client for a specific group, that +client only sees servers in that group. + +This matters because: + +- **Better AI performance**: Fewer tools means the AI can make more confident + tool selections +- **Appropriate access**: Different clients can have different capabilities + based on their purpose +- **Reduced noise**: Developers see only the tools relevant to their current + work + +For example, you might configure: + +- **Visual Studio Code** with access to your development group for day-to-day + coding +- **Claude Desktop** with access to your production group for operational tasks +- **GitHub Copilot** with access to a restricted group for code review + +:::tip + +A single client can access multiple groups. This is useful when you need tools +from several areas, like a developer who works across frontend and backend. + +::: + +## Default group behavior + +Every ToolHive installation has a `default` group that cannot be deleted. This +group serves as: + +- **The starting point**: New servers go here unless you specify otherwise +- **The fallback**: Servers move here when their group is deleted +- **The simple path**: If you don't need organization, just use the default + +You don't need to create custom groups to use ToolHive effectively. The default +group works well for simple setups and individual use. + +## Designing your group structure + +When planning your groups, consider: + +1. **Start simple**: Begin with the default group and add custom groups only + when you feel the need for organization +2. **Match your workflow**: Groups should reflect how you actually work, not an + idealized structure +3. **Consider client configuration**: Think about which clients need access to + which servers +4. **Plan for growth**: Choose a pattern that scales as you add more servers + +:::info + +Each server belongs to exactly one group. If you need the same MCP server in +multiple groups (for example, a GitHub server in both development and +production), run separate instances with different names and configurations. + +::: + +## Next steps + +Now that you understand when and why to use groups, you can: + +- [Organize servers into groups (CLI)](../guides-cli/group-management.mdx) +- [Organize servers into groups (UI)](../guides-ui/group-management.mdx) +- [Configure client access](../guides-cli/client-configuration.mdx) diff --git a/versioned_docs/version-1.0/toolhive/concepts/mcp-primer.mdx b/versioned_docs/version-1.0/toolhive/concepts/mcp-primer.mdx new file mode 100644 index 00000000..d3d98330 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/concepts/mcp-primer.mdx @@ -0,0 +1,72 @@ +--- +title: 'Model Context Protocol (MCP): A friendly primer for builders' +sidebar_label: MCP primer +description: + A brief introduction to the Model Context Protocol (MCP) and its benefits for + developers. +--- + +**TL;DR:** MCP offers a pragmatic, language-friendly bridge between +probabilistic code generators and the real-world systems where your source of +truth lives. It's young, but it already solves pain points around context size, +adapter sprawl, and brittle prompts—thanks largely to an open, welcoming +developer community. If you're building next-gen coding tools, now's the ideal +moment to give MCP a spin and leave your fingerprints on the spec. + +## Why we needed something new + +Modern code-generation models work by guessing the next token from probability +space. By nature they are powerful but probabilistic and work with natural +language. Context drives everything and they can only work on what they can see. +Most real-world context developers use lives outside the model: in GitHub repos, +API docs, RFCs, and issue trackers. Bridging that gap has been messy: + +| Traditional approach | Pain point for GenAI tools | +| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | +| Custom adapters / plugins per data source | Hard to keep in sync; brittle when schemas change | +| Prompt stuffing (copy-pasting docs into the prompt) | Dilutes effectiveness and reduces response acceptance rates, bloats token budget, hurts latency & cost | +| REST APIs with rigid schemas | Fine for deterministic code, awkward for probabilistic LLMs that prefer natural language | + +MCP tackles these headaches by letting a model **ask external systems for facts +or files using a concise, natural syntax that itself is easy for generative +models to emit and parse.** + +## What problems does MCP solve? + +- **Token-efficient context retrieval** \ + _One-shot, structured queries_ (e.g., + `mcp://github?repo=owner/project\&path=README.md`) let the model fetch exactly + what it needs—no boilerplate, no giant system prompts. +- **Natural-language-friendly envelope** \ + The URI-like syntax is short, deterministic, yet readable enough that an LLM + can generate it without dedicated training. Embeddings created before MCP work + just fine with MCP. +- **Uniform surface over heterogeneous data** \ + Git blobs, Swagger files, Confluence pages, or a private vector store all look + like "resources" under the same scheme. Tool builders write one resolver and + get many back-ends without additional work. +- **Graceful failure semantics** \ + Every MCP response carries both _content_ and a lightweight _provenance_ + object (source, timestamp, hash). Models can decide to retry, ignore, or cite. + +## The emergence of open community + +A community has sprung up around the MCP protocol incredibly quickly. + +The spec is Apache-licensed and refreshingly small, clean, and simple, which +makes the whole thing pretty easy to grok. SDK's abound and thousands of +examples exist. The efforts of communities like golang with the go-mcp release +in April 2025 are moving server development beyond the boundaries of the +traditional JavaScript and Python ecosystems. The Golang portfolio servers +inventory is growing incredibly quickly and with it comes a wealth of production +oriented access to resources. + +There's no governing foundation yet, but a lightweight steering group triages +PRs and publishes version tags. + +## Where MCP is headed + +Expect iterative, community-driven releases—v1.0 is slated for late 2025 with a +stable core and optional capability sets (search, write-back, streaming). The +protocol's youth means rough edges, but that also means **you can still shape +it**: file issues, prototype adapters, or just lurk and learn. diff --git a/versioned_docs/version-1.0/toolhive/concepts/observability.mdx b/versioned_docs/version-1.0/toolhive/concepts/observability.mdx new file mode 100644 index 00000000..f3cb8b26 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/concepts/observability.mdx @@ -0,0 +1,497 @@ +--- +title: Observability +description: Understanding ToolHive's observability features for MCP server monitoring +--- + +ToolHive provides comprehensive observability for your MCP server interactions +through built-in OpenTelemetry instrumentation. You get complete visibility into +how your MCP servers perform, including detailed traces, metrics, and error +tracking. + +## How telemetry works + +ToolHive automatically instruments your MCP server interactions without +requiring changes to your servers. When you enable telemetry, ToolHive captures +detailed information about every request, tool call, and server interaction. + +ToolHive's telemetry captures rich, protocol-aware information because it +understands MCP operations. You get detailed traces showing tool calls, resource +access, and prompt operations rather than generic HTTP requests. + +## Distributed tracing + +Distributed tracing shows you the complete journey of each request through your +MCP servers. ToolHive creates comprehensive traces that provide end-to-end +visibility across the proxy-container boundary. + +### Trace structure + +Here's what a trace looks like when a client calls a tool in the GitHub MCP +server (some fields omitted for brevity): + +```text +Span: tools/call create_issue (150ms) +├── service.name: thv-github +├── service.version: v0.1.9 +├── http.request.method: POST +├── http.request.body.size: 256 +├── http.response.status_code: 202 +├── http.response.body.size: 1024 +├── url.full: /messages?session_id=b1d22d07-b35f-4260-9c0c-b872f92f64b1 +├── url.path: /messages +├── url.scheme: https +├── server.address: localhost:14972 +├── user_agent.original: claude-code/1.0.53 +├── mcp.method.name: tools/call +├── mcp.server.name: github +├── mcp.session.id: abc123 +├── rpc.system.name: jsonrpc +├── jsonrpc.protocol.version: 2.0 +├── jsonrpc.request.id: 5 +├── gen_ai.tool.name: create_issue +├── gen_ai.operation.name: execute_tool +├── gen_ai.tool.call.arguments: owner=stacklok, repo=toolhive, pullNumber=1131 +├── network.transport: tcp +└── network.protocol.name: http +``` + +### MCP-specific traces + +ToolHive automatically captures traces for all MCP operations, including: + +- **Tool calls** (`tools/call`) - When AI assistants use tools +- **Resource access** (`resources/read`) - When servers read files or data +- **Prompt operations** (`prompts/get`) - When servers retrieve prompts +- **Connection events** (`initialize`) - When clients connect to servers + +### Trace attributes + +Each trace includes detailed context across several layers: + +#### Service information + +```text +service.name: thv-github +service.version: v0.1.9 +host.name: my-machine +``` + +#### HTTP layer + +```text +http.request.method: POST +http.request.body.size: 256 +http.response.status_code: 202 +http.response.body.size: 1024 +url.full: /messages?session_id=b1d22d07-b35f-4260-9c0c-b872f92f64b1 +url.path: /messages +url.scheme: https +url.query: session_id=b1d22d07-b35f-4260-9c0c-b872f92f64b1 +server.address: localhost:14972 +user_agent.original: claude-code/1.0.53 +``` + +#### Network layer + +```text +network.transport: tcp +network.protocol.name: http +network.protocol.version: 1.1 +client.address: 127.0.0.1 +client.port: 52431 +``` + +#### MCP protocol details + +Details about the MCP operation being performed (some fields are specific to +each operation): + +```text +mcp.method.name: tools/call +mcp.server.name: github +mcp.session.id: abc123 +mcp.protocol.version: 2025-03-26 +rpc.system.name: jsonrpc +jsonrpc.protocol.version: 2.0 +jsonrpc.request.id: 123 +``` + +#### Method-specific attributes + +- **`tools/call`** traces include: + - `gen_ai.tool.name` - The name of the tool being called + - `gen_ai.operation.name` - Set to `execute_tool` + - `gen_ai.tool.call.arguments` - Sanitized tool arguments (sensitive values + redacted) + +- **`resources/read`** traces include: + - `mcp.resource.uri` - The URI of the resource being accessed + +- **`prompts/get`** traces include: + - `gen_ai.prompt.name` - The name of the prompt being retrieved + +- **`initialize`** traces include: + - `mcp.client.name` - The name of the connecting client (always emitted) + - `mcp.protocol.version` - The MCP protocol version negotiated + +:::note[Legacy attribute names] + +By default, ToolHive emits both the new OpenTelemetry semantic convention +attribute names shown above and legacy attribute names (e.g., `http.method`, +`mcp.method`, `mcp.tool.name`) for backward compatibility with existing +dashboards. You can control this with the `--otel-use-legacy-attributes` flag. + +::: + +## Metrics collection + +ToolHive automatically collects metrics about your MCP server usage and +performance. These metrics help you understand usage patterns, performance +characteristics, and identify potential issues. + +### Metric labels + +All metrics include consistent labels for filtering and aggregation: + +- `server` - MCP server name (e.g., `fetch`, `github`) +- `transport` - Backend transport type (`stdio`, `sse`, or `streamable-http`) +- `method` - HTTP method (`POST`, `GET`) +- `mcp_method` - MCP protocol method (e.g., `tools/call`, `resources/read`) +- `status` - Request outcome (`success` or `error`) +- `status_code` - HTTP status code (`200`, `400`, `500`) +- `tool` - Tool name for tool-specific metrics + +### Key metrics + +Example metrics from the Prometheus `/metrics` endpoint are shown below (some +fields are omitted for brevity): + +#### Request metrics + +```promql +# HELP toolhive_mcp_requests_total Total number of MCP requests +# TYPE toolhive_mcp_requests_total counter +toolhive_mcp_requests_total{mcp_method="tools/list",method="POST",server="github",status="success",status_code="202",transport="stdio"} 2 + +# HELP toolhive_mcp_request_duration_seconds Duration of MCP requests in seconds +# TYPE toolhive_mcp_request_duration_seconds histogram +toolhive_mcp_request_duration_seconds_bucket{mcp_method="tools/list",method="POST",server="github",status="success",status_code="202",transport="stdio",le="10000"} 2 +toolhive_mcp_request_duration_seconds_bucket{mcp_method="tools/list",method="POST",server="github",status="success",status_code="202",transport="stdio",le="+Inf"} 2 +toolhive_mcp_request_duration_seconds_sum{mcp_method="tools/list",method="POST",server="github",status="success",status_code="202",transport="stdio"} 0.000219416 +toolhive_mcp_request_duration_seconds_count{mcp_method="tools/list",method="POST",server="github",status="success",status_code="202",transport="stdio"} 2 +``` + +#### Connection metrics + +```promql +# HELP toolhive_mcp_active_connections Number of active MCP connections +# TYPE toolhive_mcp_active_connections gauge +toolhive_mcp_active_connections{connection_type="sse",server="github",transport="stdio"} 3 +``` + +#### Tool-specific metrics + +```promql +# HELP toolhive_mcp_tool_calls_total Total number of MCP tool calls +# TYPE toolhive_mcp_tool_calls_total counter +toolhive_mcp_tool_calls_total{server="github",status="success",tool="get_file_contents"} 15 +toolhive_mcp_tool_calls_total{server="github",status="success",tool="list_pull_requests"} 4 +toolhive_mcp_tool_calls_total{server="github",status="success",tool="search_issues"} 2 +``` + +### MCP semantic convention metrics + +In addition to the ToolHive-prefixed metrics above, ToolHive emits metrics that +follow the +[OpenTelemetry MCP semantic conventions](https://github.com/open-telemetry/semantic-conventions): + +| Metric | Type | Description | +| ------------------------------- | --------- | ---------------------------------------- | +| `mcp.server.operation.duration` | Histogram | Duration of MCP server operations | +| `mcp.client.operation.duration` | Histogram | Duration of MCP client operations (vMCP) | + +These metric names follow the OpenTelemetry MCP semantic conventions in OTLP +exports and use the same labels as the ToolHive-prefixed metrics. When exposed +via the Prometheus `/metrics` endpoint, their names are converted to +Prometheus-safe form by replacing dots (`.`) with underscores (`_`): + +- `mcp.server.operation.duration` → `mcp_server_operation_duration` +- `mcp.client.operation.duration` → `mcp_client_operation_duration` + +### vMCP metrics + +When using Virtual MCP Server (vMCP), additional metrics are available for +monitoring backend operations, workflow executions, and optimizer performance. +For details, see the +[vMCP telemetry guide](../guides-vmcp/telemetry-and-metrics.mdx). + +## Trace context propagation + +ToolHive supports two methods of trace context propagation: + +- **HTTP headers**: Standard W3C Trace Context (`traceparent` and `tracestate` + headers) and W3C Baggage propagation +- **MCP `_meta` field**: Trace context embedded in MCP request parameters via + the `params._meta` field, following the MCP specification + +When both are present, the MCP `_meta` trace context takes priority. This +enables proper trace correlation across MCP server boundaries, even when MCP +clients inject trace context into the request payload rather than HTTP headers. + +## Export options + +ToolHive supports multiple export formats to integrate with your existing +observability infrastructure. + +### OTLP export + +ToolHive supports OpenTelemetry Protocol (OTLP) export for both traces and +metrics to any compatible backend, either directly or via a collector +application. + +The [OpenTelemetry ecosystem](https://opentelemetry.io/ecosystem/vendors/) +includes a wide range of observability backends including open source solutions +like Jaeger, self-hosted solutions like Splunk, and SaaS solutions like Datadog, +New Relic, and Honeycomb. + +### Prometheus export + +ToolHive can expose Prometheus-style metrics at a `/metrics` endpoint, enabling: + +- **Direct scraping** by Prometheus servers +- **Service discovery** in Kubernetes environments +- **Integration** with existing Prometheus-based monitoring stacks + +### Dual export + +Both OTLP and Prometheus can be enabled simultaneously, allowing you to: + +- Send traces to specialized tracing backends +- Expose metrics for Prometheus scraping +- Maintain compatibility with existing monitoring infrastructure + +## Data sanitization + +ToolHive automatically protects sensitive information in traces: + +- **Sensitive arguments**: Tool arguments containing passwords, tokens, or keys + are redacted +- **Sensitive key detection**: Arguments with keys containing patterns like + "password", "token", "secret", "key", "auth", or "credential" are redacted +- **Argument truncation**: Long arguments are truncated to prevent excessive + trace size + +For example, a tool call with sensitive arguments: + +```text +gen_ai.tool.call.arguments: password=secret123, api_key=abc456, title=Bug report +``` + +ToolHive sanitizes this in the trace as: + +```text +gen_ai.tool.call.arguments: password=[REDACTED], api_key=[REDACTED], title=Bug report +``` + +## Monitoring examples + +These examples show how ToolHive's observability works in practice. + +### Tool call monitoring + +When a client calls the `create_issue` tool: + +**Request**: + +```json +{ + "jsonrpc": "2.0", + "id": "req_456", + "method": "tools/call", + "params": { + "name": "create_issue", + "arguments": { + "title": "Bug report", + "body": "Found an issue with the API" + } + } +} +``` + +**Generated trace**: + +```text +Span: tools/call create_issue +├── mcp.method.name: tools/call +├── jsonrpc.request.id: req_456 +├── gen_ai.tool.name: create_issue +├── gen_ai.tool.call.arguments: title=Bug report, body=Found an issue with... +├── mcp.server.name: github +├── network.transport: tcp +├── http.request.method: POST +├── http.response.status_code: 200 +└── duration: 850ms +``` + +**Generated metrics**: + +```promql +toolhive_mcp_requests_total{mcp_method="tools/call",server="github",status="success"} 1 +toolhive_mcp_request_duration_seconds{mcp_method="tools/call",server="github"} 0.85 +toolhive_mcp_tool_calls_total{server="github",tool="create_issue",status="success"} 1 +``` + +### Error tracking + +Failed requests generate error traces and metrics: + +**Error trace**: + +```text +Span: tools/call invalid_tool +├── mcp.method.name: tools/call +├── gen_ai.tool.name: invalid_tool +├── http.response.status_code: 400 +├── span.status: ERROR +├── span.status_message: Tool not found +└── duration: 12ms +``` + +**Error metrics**: + +```promql +toolhive_mcp_requests_total{mcp_method="tools/call",server="github",status="error",status_code="400"} 1 +toolhive_mcp_tool_calls_total{server="github",tool="invalid_tool",status="error"} 1 +``` + +## Key performance indicators + +Monitor these key metrics for optimal MCP server performance: + +1. **Request rate**: `rate(toolhive_mcp_requests_total[5m])` +2. **Error rate**: `rate(toolhive_mcp_requests_total{status="error"}[5m])` +3. **Response time**: + `histogram_quantile(0.95, toolhive_mcp_request_duration_seconds_bucket)` +4. **Active connections**: `toolhive_mcp_active_connections` + +## Setting up dashboards and alerts + +This section shows practical examples of integrating ToolHive's observability +data with common monitoring tools. + +### Prometheus integration + +Configure Prometheus to scrape ToolHive metrics: + +```yaml title="prometheus.yml" +scrape_configs: + - job_name: 'toolhive-mcp-proxy' + static_configs: + - targets: [ + 'localhost:43832', # Example MCP server + 'localhost:51712' # Example MCP server + ] + scrape_interval: 15s + metrics_path: /metrics +``` + +### Grafana dashboard queries + +Example queries for monitoring dashboards: + +```promql +# Request rate by server +sum(rate(toolhive_mcp_requests_total[5m])) by (server) + +# Error rate percentage +sum(rate(toolhive_mcp_requests_total{status="error"}[5m])) by (server) / +sum(rate(toolhive_mcp_requests_total[5m])) by (server) * 100 + +# Response time percentiles +histogram_quantile(0.95, sum(rate(toolhive_mcp_request_duration_seconds_bucket[5m])) by (le, server)) + +# Tool usage distribution +sum(rate(toolhive_mcp_tool_calls_total[5m])) by (tool, server) + +# Active connections +toolhive_mcp_active_connections +``` + +### Alerting rules + +Example Prometheus alerting rules: + +```yaml title="prometheus.yml" +groups: + - name: toolhive-mcp-proxy + rules: + - alert: HighErrorRate + expr: rate(toolhive_mcp_requests_total{status="error"}[5m]) > 0.1 + for: 2m + labels: + severity: warning + annotations: + summary: 'High error rate in MCP proxy' + description: 'Error rate is {{ $value }} errors per second' + + - alert: HighResponseTime + expr: + histogram_quantile(0.95, toolhive_mcp_request_duration_seconds_bucket) + > 2.0 + for: 5m + labels: + severity: warning + annotations: + summary: 'High response time in MCP proxy' + description: '95th percentile response time is {{ $value }}s' + + - alert: ProxyDown + expr: up{job="toolhive-mcp-proxy"} == 0 + for: 1m + labels: + severity: critical + annotations: + summary: 'MCP proxy is down' + description: 'ToolHive MCP proxy has been down for more than 1 minute' +``` + +## Recommendations + +### Production deployment + +1. Use appropriate sampling rates (1-10% for high-traffic systems) +2. Configure authentication for OTLP endpoints +3. Use HTTPS transport in production +4. Monitor telemetry overhead with metrics +5. Set up alerting on key performance indicators + +### Development and testing + +1. Use 100% sampling for complete visibility +2. Enable local backends (Jaeger, Prometheus) +3. Test with realistic workloads to validate metrics +4. Verify trace correlation across service boundaries + +### Cost optimization + +1. Tune sampling rates based on traffic patterns +2. Use head-based sampling for consistent trace collection +3. Monitor backend costs and adjust accordingly +4. Filter out health check requests if not needed + +## Next steps + +Now that you understand how ToolHive's observability works, you can: + +1. **Choose a monitoring backend** that fits your needs and budget +2. **Follow the tutorial** to set up a local observability stack with + [OpenTelemetry, Jaeger, Prometheus, and Grafana](../integrations/opentelemetry.mdx) +3. **Enable telemetry** when running your servers: + - [using the ToolHive CLI](../guides-cli/telemetry-and-metrics.mdx) + - [using the Kubernetes operator](../guides-k8s/telemetry-and-metrics.mdx) +4. **Set up basic dashboards** to track request rates, error rates, and response + times +5. **Configure alerts** for critical issues + +The telemetry system works automatically once enabled, providing immediate +insights into your MCP server performance and usage patterns. diff --git a/versioned_docs/version-1.0/toolhive/concepts/registry-criteria.mdx b/versioned_docs/version-1.0/toolhive/concepts/registry-criteria.mdx new file mode 100644 index 00000000..ca51933f --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/concepts/registry-criteria.mdx @@ -0,0 +1,166 @@ +--- +title: 'Registry criteria' +description: Criteria for adding MCP servers to the ToolHive registry +--- + +The ToolHive registry is a curated list of MCP servers that meet specific +criteria. We aim to establish a curated, community-auditable list of +high-quality MCP servers through clear, observable, and objective criteria. + +## Contribute to the registry + +If you have an MCP server that you'd like to add to the ToolHive registry, you +can +[open an issue](https://github.com/stacklok/toolhive-catalog/issues/new?template=add-an-mcp-server.md) +or submit a pull request to the +[toolhive-catalog](https://github.com/stacklok/toolhive-catalog) repository. The +ToolHive team will review your submission and consider adding it to the +registry. + +Criteria for adding an MCP server to the ToolHive registry are outlined below. +These criteria ensure that the servers in the registry meet the standards of +security, quality, and usability that ToolHive aims to uphold. + +Registry entries are defined in the +[toolhive-catalog](https://github.com/stacklok/toolhive-catalog) repository. + +## Criteria for MCP servers + +### Open source requirements + +- Must be fully open source with no exceptions +- Source code must be publicly accessible +- Must use an acceptable open source license (see + [Acceptable licenses](#acceptable-licenses)) + +### Security + +- Software provenance verification (Sigstore, GitHub Attestations) +- SLSA compliance level assessment +- Pinned dependencies and GitHub Actions +- Published Software Bill of Materials (SBOMs) + +### Continuous integration + +- Automated dependency updates (Dependabot, Renovate, etc.) +- Automated security scanning +- CVE monitoring +- Code linting and quality checks + +### Repository metrics + +- Repository stars and forks +- Commit frequency and recency +- Contributor activity +- Issue and pull request statistics + +### API compliance + +- Full MCP API specification support +- Implementation of all required endpoints (tools, resources, etc.) +- Protocol version compatibility + +### Tool stability + +- Version consistency +- Breaking change frequency +- Backward compatibility maintenance + +### Code quality + +- Presence of automated tests +- Test coverage percentage +- Quality CI/CD implementation +- Code review practices + +### Documentation + +- Basic project documentation +- API documentation +- Deployment and operation guides +- Regular documentation updates + +### Release process + +- Established CI-based release process +- Regular release cadence +- Semantic versioning compliance +- Maintained changelog + +### Community health + +#### Responsiveness + +- Active maintainer engagement +- Regular commit activity +- Timely issue and pull request responses (issues open 3-4 weeks without + response is a red flag) +- Bug resolution rate +- User support quality + +#### Community strength + +- Project backing (individual vs. organizational) +- Number of active maintainers +- Contributor diversity +- Corporate or foundation support +- Governance model maturity + +### Security requirements + +#### Authentication and authorization + +- Secure authentication mechanisms +- Proper authorization controls +- Standard security protocol support (OAuth, TLS) + +#### Data protection + +- Encryption for data in transit and at rest +- Proper sensitive information handling + +#### Security practices + +- Clear incident response channels +- Security issue reporting mechanisms (email, GHSA, etc.) + +## Future considerations + +### Automated vs manual checks + +- Balance between automated checks (e.g., CI/CD, security scans) and manual + reviews (e.g., community health, documentation quality) +- Automated checks for basic compliance (e.g., license, API support) +- Manual reviews for nuanced aspects (e.g., community strength, documentation + quality) + +### Scoring system + +- **Required**: Essential attributes (significant penalty if missing) +- **Expected**: Typical well-executed project attributes (moderate score impact) +- **Recommended**: Good practice indicators (positive contribution) +- **Bonus**: Excellence demonstrators (pure positive, no penalty for absence) + +### Tiered classifications + +- "Verified" vs "Experimental/Community" designations +- Minimum threshold requirements (stars, maintainers, community indicators) +- Regular re-evaluation frequency for automated checks + +## Acceptable licenses + +The following open source licenses are accepted for MCP servers in the ToolHive +registry: + +### Permissive licenses + +Licenses such as Apache-2.0, MIT, BSD-2-Clause, and BSD-3-Clause allow maximum +flexibility for integration, modification, and redistribution with minimal +restrictions, making MCP servers accessible across all project types and +commercial applications. + +### Excluded licenses + +We exclude copyleft and restrictive licenses such as AGPL, GPL2, and GPL3 to +ensure MCP servers can be freely integrated into various commercial and open +source projects without legal complications or viral licensing requirements. diff --git a/versioned_docs/version-1.0/toolhive/concepts/skills.mdx b/versioned_docs/version-1.0/toolhive/concepts/skills.mdx new file mode 100644 index 00000000..2149c2bd --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/concepts/skills.mdx @@ -0,0 +1,113 @@ +--- +title: Understanding skills +description: + Learn what skills are in ToolHive, why they exist, and how they relate to MCP + servers. +--- + +A **skill** is a reusable, versioned bundle of instructions, prompts, and +configuration that teaches an AI agent how to perform a specific task. If MCP +servers provide **tools** (the raw capabilities an agent can call), skills +provide the **knowledge** of when, why, and how to use those tools effectively. + +## When you would use skills + +Consider these scenarios: + +- **You maintain a shared MCP registry** and want teams to publish reusable + prompt bundles alongside the MCP servers they connect to, so that other + engineers can discover both the tooling and the expertise in one place. +- **You build internal developer tools** and want to package a "code review" + workflow that combines multiple MCP server calls with domain-specific + instructions, then distribute it through a central catalog. +- **You run a platform team** and want to curate a set of approved skills that + your organization's AI agents can use, with clear versioning and status + tracking. + +## How skills relate to MCP servers + +MCP servers expose tools; skills consume them. A skill might reference one or +more tools from one or more MCP servers, wrapping them in a higher-level +workflow with context-specific instructions. + +```mermaid +flowchart LR + Skill["Skill\n(workflow + prompts)"] -->|references| MCP["MCP Server(s)\n(raw tools)"] +``` + +Skills are stored in the same Registry server instance as MCP servers, but under +a separate extensions API path (`/{registryName}/v0.1/x/dev.toolhive/skills`, +where `{registryName}` is the name of your registry). They are not intermixed +with MCP server entries. + +## Skill structure + +Each skill has the following core fields: + +| Field | Required | Description | +| ------------- | -------- | ------------------------------------------------------------------- | +| `namespace` | Yes | Reverse-DNS identifier (e.g., `io.github.acme`) | +| `name` | Yes | Skill identifier in kebab-case (e.g., `code-review`) | +| `description` | Yes | Human-readable summary of what the skill does | +| `version` | Yes | Semantic version or commit hash (e.g., `1.0.0`) | +| `status` | No | One of `ACTIVE`, `DEPRECATED`, or `ARCHIVED` (defaults to `ACTIVE`) | +| `title` | No | Human-friendly display name | +| `license` | No | SPDX license identifier (e.g., `Apache-2.0`) | + +Skills also support optional fields for packages (OCI or Git references), icons, +repository metadata, allowed tools, compatibility requirements, and arbitrary +metadata. + +### Naming conventions + +- **Namespace**: Use reverse-DNS notation. For example, `io.github.your-org` or + `com.example.team`. This prevents naming collisions across organizations. +- **Name**: Use kebab-case identifiers. For example, `code-review`, + `deploy-checker`, `security-scan`. +- **Version**: Use semantic versioning (e.g., `1.0.0`, `2.1.3`) or a commit + hash. The registry tracks a "latest" pointer per namespace/name pair. + +**Valid examples:** + +- `io.github.acme/code-review` version `1.0.0` +- `com.example.platform/deploy-checker` version `0.3.1` + +**Invalid examples:** + +- `acme/Code Review` (namespace must be reverse-DNS, name must be kebab-case) +- Empty namespace or name (both are required) + +### Package types + +Skills can reference distribution packages in two formats: + +- **OCI**: Container registry references with an identifier, digest, and media + type +- **Git**: Repository references with a URL, ref, commit, and optional subfolder + +## Versioning + +The registry stores multiple versions of each skill and maintains a "latest" +pointer. When you publish a new version, the registry automatically updates the +latest pointer if the new version is newer than the current latest. Publishing +an older version does not change the latest pointer. + +You can retrieve a specific version or request `latest` to get the most recent +one. + +## Current status and what's next + +The skills API is available as an extension endpoint on the Registry server +(`/{registryName}/v0.1/x/dev.toolhive/skills`). You can publish, list, search, +retrieve, and delete skills through this API. + +Skill installation via agent clients (such as the ToolHive CLI or IDE +extensions) is planned for a future release. For now, the registry serves as a +discovery and distribution layer where you can browse available skills and +retrieve their package references. + +## Next steps + +- [Manage skills](../guides-registry/skills.mdx) through the Registry server API +- [Registry server introduction](../guides-registry/intro.mdx) for an overview + of the Registry server diff --git a/versioned_docs/version-1.0/toolhive/concepts/tool-optimization.mdx b/versioned_docs/version-1.0/toolhive/concepts/tool-optimization.mdx new file mode 100644 index 00000000..35b5c85b --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/concepts/tool-optimization.mdx @@ -0,0 +1,260 @@ +--- +title: Optimizing LLM context with tool filtering and overrides +sidebar_label: Optimizing LLM context +description: + Understanding when and why to use tool filtering and overrides to reduce + context pollution and improve AI performance. +--- + +When AI assistants interact with MCP servers, they receive information about +every available tool. While having many tools provides flexibility, it can also +create problems. This guide explains how tool filtering and overrides help you +optimize your AI's context window for better performance and more focused +results. + +## The context pollution problem + +Modern AI clients work by analyzing all available tools and selecting the most +appropriate one for each task. This selection process happens in the AI model's +context, which means every tool's name, description, and schema consumes tokens +and processing time. + +Consider what happens when you connect an AI client to multiple MCP servers: + +- A GitHub server might expose 30+ tools for repositories, issues, pull + requests, and more +- A filesystem server adds another 10+ tools for file operations +- A database server contributes 20+ tools for queries and schema management +- Additional servers for Slack, Jira, monitoring systems, and other integrations + +Before you know it, your AI client is evaluating 100+ tools for every request. + +### Why this matters + +When your AI receives too many tools, several problems emerge: + +**Performance degradation**: More tools mean longer processing time as the AI +model evaluates each option. Tool selection becomes a bottleneck, especially for +complex queries. + +**Higher costs**: Every tool description consumes tokens. In token-based pricing +models, exposing unnecessary tools directly increases your costs for every AI +interaction. + +**Reduced accuracy**: When faced with many similar tools, AI models sometimes +choose incorrectly. A client might use a production database tool when it should +use a development one, or select a write operation when a read would suffice. + +The solution is selective tool exposure - showing your AI only the tools it +actually needs. + +## Tool filtering + +Tool filtering restricts which tools from an MCP server are available to +clients. Think of it as creating a curated subset of functionality for specific +use cases. + +### How filtering works + +ToolHive uses an allow-list approach. When you specify a filter, only the tools +you explicitly list become available. The filtering happens at the HTTP proxy +level, so: + +- The AI only sees allowed tools in its tool list +- Attempts to call filtered tools result in errors +- The backend MCP server remains unchanged + +An empty filter means all tools are available. Once you add any tool to the +filter, only listed tools are exposed. + +### When to use filtering + +Filtering makes sense in several scenarios: + +#### Improving AI tool selection + +When an MCP server exposes many tools but you only need a subset, filtering +improves the AI's ability to choose correctly. For example, enable only pull +request tools from the GitHub server when doing code review, or limit a file +system server to just `read_file` and `list_directory` for a documentation +assistant. Removing irrelevant options helps the AI make more confident +selections. + +#### Limiting access to safe operations + +An MCP server for database access might include both read and write operations. +During development or analysis, you might want to expose only read operations +like `query` and `list_tables`, while filtering out write operations like +`insert`, `update`, and `delete` that modify data or perform destructive +operations. + +#### Creating role-specific tool sets + +Different team members need different capabilities. Junior developers might get +filtered access to safe operations, while senior developers see the full tool +set. Security-sensitive tools like deployment commands might be filtered for +most users but available to DevOps engineers. + +#### Compliance and governance + +When organizational policies restrict certain operations, you can enforce policy +by only exposing approved tools, even if the underlying MCP server provides more +capabilities. + +## Tool overrides + +Tool overrides let you rename tools and update their descriptions without +modifying the backend MCP server. This is particularly valuable when tool names +are unclear or when combining multiple servers. + +### How overrides work + +Overrides maintain a bidirectional mapping between original and user-facing +names. When your AI sees the tool list, it receives the overridden names and +descriptions. When it calls a tool, ToolHive translates the user-facing name +back to the original name for the backend server. + +You can override either the name, the description, or both for each tool. + +### When to use overrides + +Overrides solve several common problems: + +#### Preventing name conflicts + +When combining multiple MCP servers through Virtual MCP Server or running +similar servers for different purposes, naming conflicts are common. Both GitHub +and Jira might have a `create_issue` tool. Overriding these to +`github_create_issue` and `jira_create_issue` eliminates ambiguity. + +When you run the same MCP server multiple times with different configurations, +tool names become identical. For example, running the GitHub server twice (once +for your company's organization and once for open source contributions) requires +renaming tools to `github_company_create_pr` and `github_oss_create_pr` to make +the distinction clear. + +#### Adding context and clarity + +Tool names and descriptions can be improved to provide environment-specific or +use-case-specific information. Renaming `deploy` to `deploy_to_staging` versus +`deploy_to_production` makes the destination explicit and reduces mistakes. +Similarly, you can update descriptions from generic text like "Deploy +application" to specific guidance like "Deploy to staging environment - +auto-rollback enabled." + +## Combining filters and overrides + +Filtering and overrides work together, but understanding their interaction is +important: **filters apply to user-facing names after overrides**. + +This means when you override a tool name, you must use the new name in your +filter list, not the original name. + +### Pattern: Secure subset with clear names + +Start by overriding technical names to be more intuitive, then filter to only +safe operations. + +```json +{ + "toolsOverride": { + "exec_raw_sql": { + "name": "run_database_query", + "description": "Execute read-only SQL queries against the staging database" + }, + "write_table": { + "name": "update_database", + "description": "Modify staging database tables (use with caution)" + } + } +} +``` + +Then filter using the new names: + +```bash +thv run --tools-override overrides.json --tools run_database_query my-db-server +``` + +**Why this works**: Clear names guide the AI while filtering enforces safety by +making destructive operations unavailable. + +### Pattern: Environment-specific configurations + +Different environments need different tool access. In development, you might +expose many tools for flexibility. In production, filter to essential tools +only. + +Your development configuration could expose all tools with friendly names +through overrides. Your production configuration uses the same overrides for +consistency but adds strict filtering to expose only read and monitoring tools, +blocking any write or deployment operations. + +**Why this works**: Consistent naming across environments with +environment-specific filtering prevents accidents while maintaining flexibility. + +### Pattern: Multi-server aggregation + +When using [Virtual MCP Server](vmcp.mdx) to combine multiple MCP servers, +overrides prevent conflicts and improve clarity: + +```json +{ + "toolsOverride": { + "search": { + "name": "github_search", + "description": "Search GitHub repositories and code" + } + } +} +``` + +You can override the `search` tool from different servers to `github_search`, +`jira_search`, and `confluence_search`. Then filter each server to its relevant +tools, creating a clean, conflict-free tool set. + +**Why this works**: Prefixes eliminate ambiguity about which server a tool +targets, while filtering prevents context overload from aggregating many +services. + +## Trade-offs to consider + +While these optimization features provide significant benefits, they also +introduce complexity: + +**Configuration and maintenance overhead**: Filters and overrides require +ongoing maintenance. When MCP servers update their tools, you'll need to adjust +your configurations. When using both features together, remember that filters +apply to overridden names, not original names. + +**Flexibility vs. safety**: Aggressive filtering makes it harder to access tools +you occasionally need. You may find yourself creating exceptions or +reconfiguring access. The more you optimize, the less flexible your system +becomes. + +**Discovery and documentation**: When tools are filtered or renamed, team +members may not realize what capabilities exist. Clear documentation becomes +essential when your visible tools don't match what the MCP server actually +provides. + +Start simple and add complexity only where it provides clear value. + +## Best practices + +**Start minimal**: Begin with a focused tool set and expand as you discover +needs, rather than filtering down from everything. + +**Be specific**: When overriding names and descriptions, provide clear, +context-specific information that helps the AI understand when to use each tool. + +## Related information + +Now that you understand when and why to use tool filtering and overrides, learn +how to configure them: + +- [Customize tools (CLI)](../guides-cli/run-mcp-servers.mdx) +- [Customize tools (UI)](../guides-ui/customize-tools.mdx) +- [Customize tools (Kubernetes)](../guides-k8s/customize-tools.mdx) +- [MCPToolConfig CRD reference](../reference/crd-spec.md) +- [Virtual MCP Server tool aggregation](../guides-vmcp/tool-aggregation.mdx) +- [Optimize tool discovery in vMCP](../guides-vmcp/optimizer.mdx) diff --git a/versioned_docs/version-1.0/toolhive/concepts/vmcp.mdx b/versioned_docs/version-1.0/toolhive/concepts/vmcp.mdx new file mode 100644 index 00000000..ff6bff74 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/concepts/vmcp.mdx @@ -0,0 +1,170 @@ +--- +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), 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 + +**Before vMCP**: Engineers manage 10+ separate MCP server connections, each with +its own authentication, manually coordinate multi-step workflows across systems, +and repeatedly configure the same parameters. + +**With vMCP**: Connect once to a unified endpoint that aggregates all backend +MCP servers, execute complex multi-system workflows declaratively, and use +pre-configured tools with sensible defaults. + +## Core value propositions + +vMCP delivers four key benefits: + +1. **Reduce complexity**: Many connections become one, dramatically simplifying + configuration +2. **Speed up workflows**: Parallel execution across systems instead of + sequential calls +3. **Improve security**: Centralized authentication and authorization with a + two-boundary model +4. **Enable reusability**: Define workflows once, use them everywhere +5. **Optimize tool discovery**: Reduce token usage by replacing all tool + definitions with two lightweight search-and-call primitives + +## Key capabilities + +### Multi-server aggregation + +Managing 10-20+ MCP server connections is overwhelming. Each server needs its +own configuration, authentication, and maintenance. vMCP aggregates all backend +MCP servers into one endpoint with automatic conflict resolution. + +vMCP supports two types of backends: + +- **MCPServer**: Container-based MCP servers running in your Kubernetes cluster +- **MCPRemoteProxy**: Proxies to external remote MCP servers (SaaS platforms, + third-party services, or MCP servers hosted outside your cluster) + +:::note[MCPRemoteProxy support] + +MCPRemoteProxy support in vMCP is currently in development. vMCP can discover +MCPRemoteProxy backends, but authentication between vMCP and MCPRemoteProxy is +not yet fully implemented. MCPServer backends work fully with vMCP. + +::: + +This architecture enables combining internal tools with external SaaS MCP +endpoints in a single unified interface. + +**Example scenario**: An engineering team needs access to 8 backend servers +(GitHub, Jira, Slack, Confluence, PagerDuty, Datadog, AWS, and internal company +docs). Some are container-based MCPServer resources running in the cluster, +while others are remote SaaS MCP endpoints accessed via MCPRemoteProxy. 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. + +When multiple backend MCP servers have tools with the same name (for example, +both GitHub and Jira have `create_issue`), vMCP automatically prefixes them: +`github_create_issue`, `jira_create_issue`. You can also define custom names for +clarity. + +### Multi-step workflows (composition) + +Real-world tasks span multiple systems and require manual orchestration. vMCP +lets you define declarative workflows with parallel execution, conditionals, +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. 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 + +Third-party MCP servers often have generic names, descriptions, and unrestricted +parameters. vMCP lets you filter, rename, and wrap tools without modifying the +upstream servers. + +**Security policy enforcement**: You can restrict a fetch tool to internal +domains only (`*.company.com`), validate URLs before calling the backend, and +provide clear error messages for policy violations. + +**Simplified interfaces**: A complex tool like AWS EC2 might have 20+ +parameters, but your frontend team only needs 3. You can create a simplified +wrapper that uses instance names instead of IDs and pre-fills safe defaults for +all other parameters. + +### Parameter defaults and pre-configuration + +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**: 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**: 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 authentication is needed for clients versus backend MCP servers, 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 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. + +## When to use vMCP + +### Good fit + +- Teams managing 5+ MCP servers (local or remote) +- Tasks requiring coordination across multiple systems +- Centralized authentication and authorization requirements +- Workflows that should be reusable across the team +- Security policies that need centralized enforcement +- Aggregating external SaaS MCP servers with internal tools + +### Not needed + +- Single MCP server usage +- Simple, one-step operations +- No orchestration requirements + +## Summary + +vMCP transforms MCP from individual servers into a unified orchestration +platform. It aggregates both container-based MCPServer resources and remote +MCPRemoteProxy backends into a single endpoint. 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 vMCP](../guides-vmcp/intro.mdx) +- [Configure authentication](../guides-vmcp/authentication.mdx) +- [Tool aggregation and conflict resolution](../guides-vmcp/tool-aggregation.mdx) +- [Composite tools and workflows](../guides-vmcp/composite-tools.mdx) +- [Optimize tool discovery](../guides-vmcp/optimizer.mdx) +- [Proxy remote MCP servers](../guides-k8s/remote-mcp-proxy.mdx) diff --git a/versioned_docs/version-1.0/toolhive/contributing.mdx b/versioned_docs/version-1.0/toolhive/contributing.mdx new file mode 100644 index 00000000..1b061c71 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/contributing.mdx @@ -0,0 +1,194 @@ +--- +title: Contributing to ToolHive +sidebar_label: Contributing +description: Learn how to contribute to ToolHive and its ecosystem of projects. +--- + +Thank you for your interest in contributing to ToolHive! This page provides an +overview of the project's architecture and links to resources for contributing +to each component. + +## Project overview + +ToolHive is composed of several independently developed components, each with +its own codebase, contributing guide, and development workflow. The components +work together to provide a complete platform for running and managing Model +Context Protocol (MCP) servers. + +All ToolHive components are open source and licensed under the Apache 2.0 +license. + +## Project components + +### ToolHive CLI, API, and Kubernetes Operator + +The core ToolHive repository contains the command-line interface, API server, +and Kubernetes operator. These components share a common codebase and provide +the runtime environment for deploying and managing MCP servers. + +**Repository**: [stacklok/toolhive](https://github.com/stacklok/toolhive) + +**Key resources**: + +- [Contributing guide](https://github.com/stacklok/toolhive/blob/main/CONTRIBUTING.md) +- [Developer documentation](https://github.com/stacklok/toolhive/blob/main/docs/README.md) +- [Kubernetes Operator developer guide](https://github.com/stacklok/toolhive/blob/main/cmd/thv-operator/README.md) +- [Issue tracker](https://github.com/stacklok/toolhive/issues) + +### ToolHive desktop UI + +The ToolHive Desktop UI is a cross-platform application that provides a +graphical interface for discovering, installing, and managing MCP servers. It's +built with TypeScript and Electron, and runs on macOS, Windows, and Linux. + +**Repository**: +[stacklok/toolhive-studio](https://github.com/stacklok/toolhive-studio) + +**Key resources**: + +- [Contributing guide](https://github.com/stacklok/toolhive-studio/blob/main/CONTRIBUTING.md) +- [Developer documentation](https://github.com/stacklok/toolhive-studio/blob/main/docs/README.md) +- [Issue tracker](https://github.com/stacklok/toolhive-studio/issues) + +### ToolHive Cloud UI + +The ToolHive Cloud UI is a web application for visualizing MCP servers running +in user infrastructure with easy URL copying for integration with AI agents. +Built with Next.js and TypeScript, this is an experimental project that is +actively being developed and tested. + +**Repository**: +[stacklok/toolhive-cloud-ui](https://github.com/stacklok/toolhive-cloud-ui) + +**Key resources**: + +- [Contributing guide](https://github.com/stacklok/toolhive-cloud-ui/blob/main/CONTRIBUTING.md) +- [Issue tracker](https://github.com/stacklok/toolhive-cloud-ui/issues) + +### Registry server + +The Registry Server is an API server that implements the official MCP Registry +API. It provides standardized access to MCP servers from multiple backends, +including file-based and other API-compliant registries. + +**Repository**: +[stacklok/toolhive-registry-server](https://github.com/stacklok/toolhive-registry-server) + +**Key resources**: + +- [Contributing guide](https://github.com/stacklok/toolhive-registry-server/blob/main/CONTRIBUTING.md) +- [Issue tracker](https://github.com/stacklok/toolhive-registry-server/issues) + +### Built-in registry + +The built-in registry contains the curated list of MCP servers available in +ToolHive. If you want to add a new MCP server to the registry, this is the +repository to contribute to. + +**Repository**: +[stacklok/toolhive-catalog](https://github.com/stacklok/toolhive-catalog) + +**Key resources**: + +- [Contributing guide](https://github.com/stacklok/toolhive-catalog/blob/main/CONTRIBUTING.md) +- [Registry inclusion criteria](./concepts/registry-criteria.mdx) +- [Submit a new MCP server](https://github.com/stacklok/toolhive-catalog/issues/new?template=add-an-mcp-server.md) +- [Issue tracker](https://github.com/stacklok/toolhive-catalog/issues) + +### Dockyard + +Dockyard is a tool that packages MCP servers into containers. It handles the +containerization process, making it easy to run MCP servers in isolated +environments. + +**Repository**: [stacklok/dockyard](https://github.com/stacklok/dockyard) + +**Key resources**: + +- [Contributing guide](https://github.com/stacklok/dockyard/blob/main/CONTRIBUTING.md) +- [Issue tracker](https://github.com/stacklok/dockyard/issues) + +### MCP Optimizer + +The MCP Optimizer acts as an intelligent intermediary between AI clients and MCP +servers. It provides tool discovery, unified access to multiple MCP servers +through a single endpoint, and intelligent routing. The optimizer reduces token +usage by narrowing down the toolset to only relevant tools for each request. + +**Repository**: +[StacklokLabs/mcp-optimizer](https://github.com/StacklokLabs/mcp-optimizer) + +**Key resources**: + +- [Contributing guide](https://github.com/StacklokLabs/mcp-optimizer/blob/main/CONTRIBUTING.md) +- [Usage tutorial](./tutorials/mcp-optimizer.mdx) +- [Documentation](https://github.com/StacklokLabs/mcp-optimizer/tree/main/docs) +- [Issue tracker](https://github.com/StacklokLabs/mcp-optimizer/issues) + +### Documentation website + +The documentation website (this site) is built with Docusaurus and contains +guides, tutorials, and reference documentation for all ToolHive components. + +**Repository**: +[stacklok/docs-website](https://github.com/stacklok/docs-website) + +**Key resources**: + +- [Contributing guide](https://github.com/stacklok/docs-website/blob/main/CONTRIBUTING.md) +- [Writing style guide](https://github.com/stacklok/docs-website/blob/main/STYLE-GUIDE.md) +- [Issue tracker](https://github.com/stacklok/docs-website/issues) + +## Ways to contribute + +We welcome contributions of all kinds, and no contribution is too small. Whether +you're fixing a typo, improving documentation, or adding a major feature, your +help is appreciated. First-time contributors are especially welcome! + +Here are some ways you can contribute to ToolHive: + +- **Code**: Fix bugs, add features, or improve performance in any of the project + components. +- **Documentation**: Write guides, improve existing docs, add examples, or fix + typos. +- **Testing**: Test new features, report bugs, or contribute automated tests. +- **Bug triage**: Help review and reproduce issues reported by others. +- **Examples and tutorials**: Create sample projects or write tutorials showing + how to use ToolHive. +- **MCP servers**: Submit new MCP servers to the registry or improve existing + ones. +- **Community support**: Help answer questions in GitHub issues or on Discord. +- **Design and UX**: Contribute to the UI design or suggest improvements to the + user experience. + +## Getting started + +Ready to contribute? Here are some ways to get involved: + +1. **Explore the codebase**: Browse the repositories that interest you and read + through the contributing guides to understand the development workflow. + +2. **Find an issue**: Look for issues labeled `good first issue` or + `help wanted` in the issue trackers. These are great starting points for new + contributors. + +3. **Join the community**: Connect with other contributors in the + [Stacklok Discord](https://discord.gg/stacklok) community. The + `#toolhive-developers` channel is dedicated to technical discussions. + +4. **Report bugs**: If you find a bug, open an issue in the appropriate + repository with a clear description and steps to reproduce. + +5. **Suggest features**: Have an idea for a new feature? Open an issue to + discuss it with the maintainers before starting work. + +## Code of conduct + +All contributors are expected to follow the +[Stacklok Code of Conduct](https://github.com/stacklok/toolhive/blob/main/CODE_OF_CONDUCT.md). +We're committed to providing a welcoming and inclusive environment for everyone. + +## Additional resources + +- [Frequently asked questions](./faq.mdx) +- [Stacklok Discord community](https://discord.gg/stacklok) diff --git a/versioned_docs/version-1.0/toolhive/enterprise.mdx b/versioned_docs/version-1.0/toolhive/enterprise.mdx new file mode 100644 index 00000000..b949af93 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/enterprise.mdx @@ -0,0 +1,322 @@ +--- +title: Stacklok Enterprise +description: Stacklok Enterprise offerings for ToolHive +--- + +import HubSpotForm from '@site/src/components/HubSpotForm'; +import Heading from '@theme/Heading'; +import BrandedList from '@site/src/components/BrandedList'; + + + A hardened and production-ready distribution of ToolHive Community + + +Securely scale MCP servers across your enterprise with Stacklok Enterprise's +signed binaries, hardened images, formal semantic versioning, backported +security patches, and turnkey identity provider integrations. Kubernetes native +and LLM agnostic. Self-hosted in your environment, governed by your policies, no +vendor lock-in. + + + +--- + +

+ Running in production at major financial services, technology, and software + companies,
+ including Fortune 500 and Global 2000 enterprises +

+ +--- + +## When Community isn't enough + +Teams typically move to Stacklok Enterprise when they hit one of these walls: + + + +- Developers are bringing their own MCP servers to work — shadow AI is spreading + and there is no central control +- Your organization has multiple coding assistants and AI agents that need + access to business context +- Your security or compliance team is asking how MCP servers are authenticated, + audited, and patched +- You need SSO and IdP integration (Okta, Entra ID) across your organization +- You are running MCP in production and need SLA-backed support for incidents +- You need centralized governance and policy enforcement across multiple teams + or business units +- Your environment requires a semantically versioned, supply-chain-attested + distribution rather than continuous rolling release + + + +Recognizing these challenges in your organization? +[Schedule a demo](#schedule-a-demo) to see how Stacklok Enterprise addresses +them. + +--- + +## ToolHive Community vs. Stacklok Enterprise + +### Distribution & packaging + +| Capability | Community | Enterprise | +| :-------------------------------------------------------- | :--------: | :----------------------------------------: | +| ToolHive core platform | ✓ | ✓ | +| Release model | Continuous | Semantically versioned (MAJOR.MINOR.PATCH) | +| SigStore Cosign package signing with SBOM | ✓ | ✓ | +| Patch versions retained for bugfixes and security updates | — | ✓ | +| Scanning attestations | — | ✓ | +| SLSA build provenance | — | ✓ | + +### Security and supply chain + +| Capability | Community | Enterprise | +| :------------------------------------------------------- | :-------: | :--------: | +| Basic scanning (Trivy, unit tests, integration tests) | ✓ | ✓ | +| Static analysis on every release (attested via SigStore) | — | ✓ | +| Autonomous pen testing on every minor release | — | ✓ | +| Hardened container base images (Chainguard or equiv.) | — | ✓ | +| Proactive notification of vulnerabilities | — | ✓ | +| CVEs addressed within SLO with responsible disclosure | — | ✓ | +| All Sev 0-3 vulnerabilities backported as patch updates | — | ✓ | + +### Auth, identity & governance + +| Capability | Community | Enterprise | +| :---------------------------------------------------- | :-------: | :--------: | +| Basic authentication | ✓ | ✓ | +| Policy-as-code engine (CEDAR) | ✓ | ✓ | +| Audit logging & compliance reporting | ✓ | ✓ | +| Built-in IdP integration (Okta, Entra ID) | — | ✓ | +| IdP group → ToolHive role mapping | — | ✓ | +| Canonical policy packs (read-only, full CRUD, custom) | — | ✓ | +| Token exchange & credential brokering | — | ✓ | + +### Enterprise UI & management + +| Capability | Community | Enterprise | +| :------------------------------------------------- | :-------: | :--------: | +| ToolHive CLI | ✓ | ✓ | +| Usage telemetry & analytics (OpenTelemetry) | ✓ | ✓ | +| Enterprise MCP registry server and catalog | ✓ | ✓ | +| Enterprise Cloud UI (full CRUD management console) | — | ✓ | +| Hardened Desktop UI (enterprise lockdown controls) | — | ✓ | + +### Versioning, maintenance & support + +| Capability | Community | Enterprise | +| :--------------------------------------------- | :-------: | :--------: | +| Latest release | ✓ | ✓ | +| Supported versions: LATEST, LATEST-1, LATEST-2 | — | ✓ | +| Community support (GitHub) | ✓ | ✓ | +| Dedicated support with SLA | — | ✓ | +| Proactive security advisories | — | ✓ | +| Onboarding & integration assistance | — | ✓ | + +### Enterprise Connectors (MCP Servers) + +| Attribute | Community | Enterprise | +| :------------------------------------------ | :---------: | :----------------------------------: | +| Base image | Open source | Chainguard or equivalent | +| Signing & attestations | — | SigStore signed with SLSA provenance | +| Customized tools (tuned to agent workflows) | — | ✓ | +| Streamable HTTP transport | — | ✓ | +| SBOM & dependency vetting | — | ✓ | +| Qualified for target workload | — | ✓ | +| Maintained on enterprise release cadence | — | ✓ | +| Backported security patches | — | ✓ | + +Seen enough to want a closer look? [Schedule a demo](#schedule-a-demo) to walk +through the capabilities that matter most to your team. + +--- + +## Product offerings + +Stacklok aims to keep pricing and licensing simple. Stacklok Enterprise and its +Enterprise Connectors are licensed as an annual subscription. Professional +services are priced based on time and materials. + +| SKU | Description | Pricing Model | +| :------------------------------- | :----------------------------------------------------------------------------------------------------------------------------- | :---------------------------------: | +| **Stacklok Enterprise Platform** | Enterprise licensed distribution of ToolHive with Cloud UI, Desktop UI, IdP integration, policy engine, and SLA-backed support | Annual subscription | +| **Enterprise Connectors** | Production-ready connectors, maintained on enterprise release cadence | Annual subscription (per connector) | +| **Professional Services** | Extended integration, policy configuration, additional IdP onboarding, connector development | Time & materials | + +Ready to discuss what the right package looks like for your organization? +[Schedule a demo](#schedule-a-demo) to talk through your requirements. + +--- + +## Enterprise Platform Components + +Stacklok Enterprise Platform secures MCP servers across your organization +through its registry, runtime, gateway, and portal. + +### Registry: No more fighting shadow AI + +| The source of truth for approved MCP servers within the enterprise. | +| :----------------------------------------------------------------------- | +| Integrate with the official MCP registry | +| Add custom MCP servers and skills | +| Group servers based on role or use case | +| Manage your registry with an API-driven interface | +| Verify provenance and sign servers with built-in security controls | +| Preset configurations and permissions for a frictionless user experience | + +### Runtime: Kubernetes-native deployment + +| Deploy, run, and manage MCP servers in Kubernetes with security guardrails. | +| :-------------------------------------------------------------------------- | +| Deploy MCP servers in the cloud via Kubernetes | +| Run MCP servers locally via Docker or Podman | +| Proxy remote MCP servers securely for unified management | +| Kubernetes Operator for fleet and resource management | +| Leverage OpenTelemetry for centralized monitoring and audit logging | + +### Gateway: Single endpoint, full control + +| Intelligent MCP gateway for authentication, authorization, and policy enforcement. | +| :--------------------------------------------------------------------------------------- | +| Integrate with your IdP for SSO (OIDC/OAuth compatible) | +| Build composite tools that orchestrate multiple tools in parallel or sequential chains | +| Customize and filter tools and descriptions | +| Reduce context bloat and token usage | +| Connect with local clients like Claude Desktop, Cursor, and Visual Studio Code (VS Code) | + +### Portal: Self-service with guardrails + +| Custom UI for teams to discover, deploy and manage approved MCP servers. | +| :----------------------------------------------------------------------- | +| Cross-platform desktop app and web-based cloud UI | +| Make it easy for admins to curate MCP servers and tools | +| Automate server discovery | +| Install MCP servers with a single click | +| Compatible with hundreds of AI clients | + +Ready to see how the platform works in your environment? +[Start a proof of concept](#validate-stacklok-enterprise-in-your-environment) to +take the next step. + +--- + +## Validate Stacklok Enterprise in your environment + +Stacklok helps you validate Stacklok Enterprise in your environment at your pace +with forward-deployed engineering support. + + + +--- + +## Frequently asked questions + +
+How does Stacklok Enterprise relate to ToolHive Community? + +ToolHive Community is an open source distribution optimized for individual +developers and pre-production use, making it the right tool for evaluating MCP +and building a proof of concept. Stacklok Enterprise is a separate, hardened +distribution built for production: semantically versioned, with IdP integration, +centralized governance, and SLA-backed support. Moving from Community to +Enterprise is a supported migration where Stacklok provides the enterprise +binaries and dedicated engineering support to take you from proof of concept to +production. +[See the full comparison](#toolhive-community-vs-stacklok-enterprise) or +[learn about the proof of concept engagement](#validate-stacklok-enterprise-in-your-environment). + +
+ +
+What happens to my data if I end my Enterprise contract? + +Your data never leaves your environment. Stacklok Enterprise is fully +self-hosted: you retain complete control over your data and infrastructure, +regardless of contract status. If you end your subscription, you can downgrade +to the open-source version at any time. The only things you lose are access to +Enterprise features, forward-deployed engineers, backported security patches, +and dedicated support. There is zero vendor lock-in. +[Learn more about the product offerings](#product-offerings). + +
+ +
+How long does a typical deployment take? + +Most customers begin to see value in less than 2 weeks of contract signing. +Stacklok works directly with your platform team, and every Enterprise license +includes dedicated engineering support throughout the process. You will need an +existing Kubernetes environment to get started. Timelines are scoped to your +environment, so if your situation is more complex, Stacklok will work at your +pace. +[Learn about the proof of concept engagement](#validate-stacklok-enterprise-in-your-environment). + +
+ +
+Why should I use an MCP platform instead of running MCP servers directly? + +Running MCP servers directly gives you no isolation, no access controls, and no +visibility into what those servers are doing. Stacklok Enterprise addresses this +by running each server in its own container with least-privilege permissions, +encrypting credentials at rest, and tracing every tool call via OpenTelemetry. +Stacklok Enterprise adds centralized governance, IdP-backed authentication, and +audit logging for teams running MCP at scale across their organization. +[Explore the core concepts](./concepts/) to dig deeper into how ToolHive works. + +
+ +
+What AI clients work with Stacklok Enterprise? + +Stacklok Enterprise works with any AI coding assistant or agent that supports +MCP. This includes Claude Code, GitHub Copilot, Cursor, Windsurf, VS Code, Zed, +Cline, Continue, Roo Code, Goose, LM Studio, OpenAI Codex, and many more. Most +clients support automatic configuration so developers can connect without manual +setup. +[See the full client compatibility reference](./reference/client-compatibility.mdx) +for the complete list. + +
+ +
+Can I run custom MCP servers outside the Stacklok registry? + +Yes. Stacklok Enterprise starts with a base registry of vetted, hardened MCP +servers maintained by Stacklok. From there, you have full control to add your +own servers from public package managers, Docker images, remote URLs, or build a +private registry tailored to your organization. You are never limited to +Stacklok's catalog. +[See how to run MCP servers in Kubernetes](./guides-k8s/run-mcp-k8s.mdx) for the +full details. + +
+ +--- + +## Explore ToolHive Community + +:::tip[Not ready for Stacklok Enterprise yet?] + +ToolHive Community is free, open source, and the best way to evaluate MCP before +moving to production. + +[Get started with ToolHive Community →](./index.mdx) + +::: diff --git a/versioned_docs/version-1.0/toolhive/faq.mdx b/versioned_docs/version-1.0/toolhive/faq.mdx new file mode 100644 index 00000000..5a4d1fc6 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/faq.mdx @@ -0,0 +1,237 @@ +--- +title: Frequently asked questions +sidebar_label: FAQ +description: Common questions about ToolHive. +--- + +## General questions + +### What is ToolHive? + +ToolHive is an open source platform that simplifies the deployment and +management of Model Context Protocol (MCP) servers. It runs MCP servers in +secure, isolated containers and provides tools to manage them easily. You can +run ToolHive as a graphical desktop app, command-line tool, or Kubernetes +operator. + +### What is the Model Context Protocol (MCP)? + +MCP is a protocol that allows AI applications to connect to external data +sources and tools. It provides a standardized way for AI models to access +real-world context like APIs, source code repositories, databases, and other +systems. Think of it as a bridge between AI models and the external systems +where your data and applications live. + +### Do I need to know how to code to use ToolHive? + +No. ToolHive includes a graphical interface that doesn't require coding +knowledge. However, some MCP servers may require configuration or secrets, such +as API keys, that you'll need to set up. The ToolHive CLI is more technical but +comes with comprehensive documentation. + +### Is ToolHive free to use? + +Yes, ToolHive is open source (Apache 2.0 licensed) and free to use. You can find +the source code on GitHub and use it without any licensing fees. + +### Can I use ToolHive UI and the CLI together? + +Yes, but ToolHive UI manages the CLI automatically. When you install ToolHive +UI, it creates a symlink to its bundled CLI and configures your PATH. You can +use the `thv` command from your terminal—it uses the UI-managed version. + +If you have a standalone CLI installed separately (via Homebrew, WinGet, or +manually), it will conflict with the UI-managed version and show an error. +Uninstall the standalone version to resolve this. + +For more information, see the [CLI access guide](./guides-ui/cli-access.mdx) and +[CLI conflict resolution](./guides-cli/install.mdx#cli-conflict-resolution). + +## Using MCP servers + +### How do I find available MCP servers? + +ToolHive includes a curated registry of verified MCP servers. You can browse +them from the **Registry** tab in the UI or by running `thv registry list` in +the CLI. + +### What MCP servers are available? + +The registry includes servers for common use cases, such as retrieving content +from the web using the +[GoFetch MCP server](https://github.com/StacklokLabs/gofetch), and for popular +services and platforms like: + +- **Atlassian** – Access Jira and Confluence +- **AWS Documentation** – Query AWS service documentation +- **GitHub** – Access repositories, issues, and pull requests +- **Kubernetes** – Interact with Kubernetes clusters via the + [MKP MCP server](https://github.com/StacklokLabs/mkp) +- **MongoDB**, **PostgreSQL**, **Redis** – Connect to databases +- **Notion** – Connect to Notion workspaces +- And many more + +### Can I run MCP servers that aren't in the registry? + +Yes. You can run any MCP server from a container image or source package, even +if it's not in the registry. Just provide the image name or package details when +starting the server using the CLI or UI. See the custom MCP server section in +the [UI guide](./guides-ui/run-mcp-servers.mdx#install-a-custom-mcp-server) and +[CLI guide](./guides-cli/run-mcp-servers.mdx#run-a-custom-mcp-server) for more +details. + +The Kubernetes operator also supports custom MCP servers that are packaged as +container images. + +:::tip + +You can use the ToolHive CLI to run a custom MCP server from a source package +once, then export the Docker image for import into your container registry or +Kubernetes cluster to use it with the operator. + +::: + +## Privacy and data collection + +### Does ToolHive collect any data? + +ToolHive collects anonymous usage metrics to help improve the product. These +metrics include only tool call counts and are completely anonymous. No personal +information, user identifiers, or sensitive data is collected. + +The metrics collection: + +- Is enabled by default +- Only tracks the number of tool calls +- Uses a randomly generated instance ID (not tied to your identity) +- Is automatically disabled in CI environments +- Can be easily disabled + +### How do I disable usage metrics? + +You can opt out of usage metrics collection in two ways: + +**Option 1: Persistent configuration (recommended)** + +Use the ToolHive CLI to disable metrics permanently: + +```bash +thv config usage-metrics disable +``` + +**Option 2: Environment variable** + +Set an environment variable to disable metrics for the current session: + +```bash +export TOOLHIVE_USAGE_METRICS_ENABLED=false +``` + +Once you opt out, ToolHive stops collecting and sending usage metrics. You need +to restart any running servers for the change to take effect. + +## Security and permissions + +### Is it safe to run MCP servers? + +ToolHive runs MCP servers in isolated containers with minimal default +permissions. Each server runs in its own container with restricted access to +your system and network. + +:::tip + +For extra security, review the permission profiles and network isolation options +before running new or untrusted MCP servers. + +::: + +### How does ToolHive handle secrets like API keys? + +ToolHive provides secure secrets management: + +- Secrets are encrypted and stored securely on your system +- They're passed to MCP servers as environment variables +- Secrets never appear in plaintext in configuration files +- Integration with 1Password is also supported + +### Can I control what an MCP server can access? + +Yes. ToolHive uses permission profiles to control: + +- **File system access** – Which directories the server can read or write +- **Network access** – Which hosts and ports the server can connect to + +You can use built-in profiles or create custom ones for specific security +requirements. + +### What's network isolation and when should I use it? + +Network isolation creates a secure network architecture that filters all +outbound connections from MCP servers. Use the `--isolate-network` flag when +running servers that need strict network controls, especially in enterprise +environments. + +## Enterprise and advanced usage + +### Can I use ToolHive in my company? + +Yes. ToolHive is designed for both individual developers and enterprise teams. +The Kubernetes Operator provides centralized management, security controls, and +integration with existing infrastructure for enterprise deployments. + +### How do I deploy ToolHive in Kubernetes? + +Use the ToolHive Kubernetes Operator to deploy and manage MCP servers as +Kubernetes resources. See the [Kubernetes guides](./guides-k8s/index.mdx) for +detailed instructions. The Kubernetes operator is currently experimental. + +### Can I run my own custom MCP servers? + +Yes. You can run custom MCP servers from Docker images or source packages. +ToolHive supports: + +- Docker images from public or private registries +- Source packages from package managers like npm, PyPI, or Go modules + +### How do I get my MCP server added to the ToolHive registry? + +The ToolHive registry has specific +[inclusion criteria](./concepts/registry-criteria.mdx), such as being open +source, following good security practices, and maintaining code quality. Review +the criteria and +[submit your server for consideration](https://github.com/stacklok/toolhive-catalog/issues/new?template=add-an-mcp-server.md). + +### Can I use ToolHive behind a corporate firewall? + +Yes. ToolHive supports corporate environments with: + +- Custom CA certificate configuration for TLS inspection +- Network isolation and permission profiles +- Integration with secret management systems like 1Password + +## Getting help + +### Where can I get help if I'm stuck? + +- **Documentation** – Check the comprehensive guides and reference documentation +- **GitHub Issues** – Report bugs or request features on the + [ToolHive GitHub repository](https://github.com/stacklok/toolhive/issues) +- **Discord Community** – Join the + [Stacklok Discord](https://discord.gg/stacklok) for community support +- **Troubleshooting sections** – Each guide includes troubleshooting tips for + common issues + +### How do I report a bug or request a feature? + +Open an issue in the appropriate GitHub repository: + +- [**ToolHive UI**](https://github.com/stacklok/toolhive-studio/issues) – For + issues specific to the graphical desktop app +- [**ToolHive CLI & Kubernetes**](https://github.com/stacklok/toolhive/issues) – + For issues related to the CLI tool or Kubernetes operator + +### Is there a community I can join? + +Yes! Join the [Stacklok Discord community](https://discord.gg/stacklok) to +connect with other ToolHive users, ask questions, and share your experiences. +There's a dedicated `#toolhive-developers` channel for technical discussions. diff --git a/versioned_docs/version-1.0/toolhive/guides-cli/advanced-cicd.mdx b/versioned_docs/version-1.0/toolhive/guides-cli/advanced-cicd.mdx new file mode 100644 index 00000000..f803324f --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-cli/advanced-cicd.mdx @@ -0,0 +1,318 @@ +--- +title: Advanced CI/CD patterns +description: + Advanced CI/CD patterns for building and deploying MCP server containers with + ToolHive. +--- + +This guide covers advanced CI/CD patterns for building MCP server containers +using ToolHive's [`thv build`](../reference/cli/thv_build.md) command. These +patterns include multi-architecture builds, supply chain security, and efficient +change detection. + +These patterns are intended for anyone building MCP server containers regularly, +such as maintainers of MCP servers or organizations deploying custom servers or +repackaging existing ones. + +## Prerequisites + +Before implementing these advanced patterns, ensure you have: + +- Basic understanding of [`thv build`](./build-containers.mdx) command +- Experience with CI/CD pipelines (GitHub Actions, GitLab CI, etc.) +- Container registry access for pushing images +- Understanding of Docker Buildx for multi-architecture builds + +## Multi-architecture MCP server builds + +Build MCP server containers for multiple architectures (amd64, arm64) using +Docker Buildx: + +```yaml +name: Multi-arch Build +on: + push: + tags: ['v*'] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Install ToolHive + uses: StacklokLabs/toolhive-actions/install@v0 + with: + version: latest + + - name: Generate Dockerfile + run: | + thv build --dry-run --output Dockerfile uvx://mcp-server-git + + - name: Build multi-arch container + run: | + docker buildx build \ + --platform linux/amd64,linux/arm64 \ + --tag ghcr.io/myorg/mcp-server:${{ github.ref_name }} \ + --push \ + . +``` + +## Supply chain security + +Enhance security with SBOM generation, provenance attestation, and image +signing: + +```yaml +name: Secure Build +on: + push: + tags: ['v*'] + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + id-token: write + steps: + - uses: actions/checkout@v4 + + - name: Install Cosign + uses: sigstore/cosign-installer@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Install ToolHive + uses: StacklokLabs/toolhive-actions/install@v0 + with: + version: latest + + - name: Generate Dockerfile + run: | + thv build --dry-run --output Dockerfile uvx://mcp-server-git + + - name: Build with security features + uses: docker/build-push-action@v6 + id: build + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + tags: ghcr.io/myorg/mcp-server:${{ github.ref_name }} + sbom: true # Generate Software Bill of Materials + provenance: true # Generate build provenance + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Sign container image + env: + DIGEST: ${{ steps.build.outputs.digest }} + run: | + cosign sign --yes ghcr.io/myorg/mcp-server@${DIGEST} +``` + +## Efficient change detection + +Build only when relevant files change to optimize CI/CD performance: + +```yaml +name: Conditional Build +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + detect-changes: + runs-on: ubuntu-latest + outputs: + build_needed: ${{ steps.changes.outputs.build_needed }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Detect changes + id: changes + run: | + # Check if MCP server configs or Dockerfiles changed + if git diff --name-only HEAD~1..HEAD | grep -E "(mcp-configs/|Dockerfile|\.thv)"; then + echo "build_needed=true" >> $GITHUB_OUTPUT + else + echo "build_needed=false" >> $GITHUB_OUTPUT + fi + + build: + needs: detect-changes + if: needs.detect-changes.outputs.build_needed == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install ToolHive + uses: StacklokLabs/toolhive-actions/install@v0 + with: + version: latest + + - name: Build containers + run: | + thv build --tag ghcr.io/myorg/mcp-server:latest uvx://mcp-server-git +``` + +## Matrix builds for multiple servers + +Build multiple MCP servers in parallel using matrix strategies: + +```yaml +name: Matrix Build +on: + push: + tags: ['v*'] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + server: + - name: git-server + scheme: uvx://mcp-server-git + - name: filesystem-server + scheme: npx://@modelcontextprotocol/server-filesystem + - name: custom-server + scheme: go://github.com/myorg/custom-mcp-server@latest + steps: + - uses: actions/checkout@v4 + + - name: Install ToolHive + uses: StacklokLabs/toolhive-actions/install@v0 + with: + version: latest + + - name: Build ${{ matrix.server.name }} + run: | + thv build --tag ghcr.io/myorg/${{ matrix.server.name }}:${{ github.ref_name }} \ + ${{ matrix.server.scheme }} + + - name: Push ${{ matrix.server.name }} + run: | + docker push ghcr.io/myorg/${{ matrix.server.name }}:${{ github.ref_name }} +``` + +## Vulnerability scanning + +Integrate security scanning into your build pipeline: + +```yaml +name: Secure Build with Scanning +on: + push: + tags: ['v*'] + +jobs: + build-and-scan: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install ToolHive + uses: StacklokLabs/toolhive-actions/install@v0 + with: + version: latest + + - name: Build container + run: | + thv build --tag mcp-server:scan uvx://mcp-server-git + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@master + with: + image-ref: 'mcp-server:scan' + format: 'sarif' + output: 'trivy-results.sarif' + + - name: Upload Trivy scan results + uses: github/codeql-action/upload-sarif@v3 + if: always() + with: + sarif_file: 'trivy-results.sarif' + + - name: Tag and push if scan passes + run: | + docker tag mcp-server:scan ghcr.io/myorg/mcp-server:${{ github.ref_name }} + docker push ghcr.io/myorg/mcp-server:${{ github.ref_name }} +``` + +## GitLab CI example + +For GitLab CI users, here's an equivalent pipeline: + +```yaml +# .gitlab-ci.yml +stages: + - build + - security + - deploy + +variables: + DOCKER_DRIVER: overlay2 + DOCKER_TLS_CERTDIR: '/certs' + +build: + stage: build + image: docker:latest + services: + - docker:dind + before_script: + # Install ToolHive CLI using curl and jq to get the latest version + - | + VERSION=$(curl -s https://api.github.com/repos/stacklok/toolhive/releases/latest | jq -r .tag_name) + wget "https://github.com/stacklok/toolhive/releases/download/${VERSION}/toolhive_${VERSION#v}_linux_amd64.tar.gz" + tar -xzf "toolhive_${VERSION#v}_linux_amd64.tar.gz" + install -m 0755 thv /usr/local/bin/ + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + script: + - thv build --tag $CI_REGISTRY_IMAGE/mcp-server:$CI_COMMIT_TAG + uvx://mcp-server-git + - docker push $CI_REGISTRY_IMAGE/mcp-server:$CI_COMMIT_TAG + only: + - tags + +security_scan: + stage: security + image: aquasec/trivy:latest + script: + - trivy image --exit-code 1 --severity HIGH,CRITICAL + $CI_REGISTRY_IMAGE/mcp-server:$CI_COMMIT_TAG + only: + - tags +``` + +## Best practices + +When implementing advanced CI/CD patterns: + +1. **Use specific tags** instead of `latest` for production deployments +2. **Implement proper caching** to speed up builds +3. **Scan for vulnerabilities** before pushing to production registries +4. **Sign images** for supply chain security +5. **Use matrix builds** for multiple MCP servers +6. **Implement change detection** to avoid unnecessary builds +7. **Store sensitive data** in CI/CD secrets, not in code + +## Related information + +- [Build MCP server containers](./build-containers.mdx) +- [Run MCP servers in Kubernetes](../guides-k8s/run-mcp-k8s.mdx) +- [`thv build` command reference](../reference/cli/thv_build.md) +- [Secrets management](./secrets-management.mdx) diff --git a/versioned_docs/version-1.0/toolhive/guides-cli/api-server.mdx b/versioned_docs/version-1.0/toolhive/guides-cli/api-server.mdx new file mode 100644 index 00000000..ca8de827 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-cli/api-server.mdx @@ -0,0 +1,89 @@ +--- +title: Run the API server +description: How to run the local ToolHive API server. +--- + +ToolHive includes a built-in API server that provides a RESTful interface for +interacting with MCP servers. The API server is useful for integrating ToolHive +with other applications or automating tasks. + +:::note + +The API server isn't intended for production use. It's designed for local +automation and UI development, and doesn't implement any authentication or +authorization mechanisms. + +For production use cases, consider using the ToolHive Kubernetes operator, which +provides a more robust and secure way to manage ToolHive instances in a +multi-user environment. + +::: + +## Start the API server + +To start the API server, use the following command: + +```bash +thv serve +``` + +This starts the API server on `localhost` (127.0.0.1) using the default port +`8080`. + +Test the API server using `curl` or a web browser: + +```bash +curl http://localhost:8080/api/v1beta/status +``` + +You should see a JSON response with the current ToolHive version. + +## Custom networking + +By default, the API server listens on `localhost` (127.0.0.1) port `8080`. + +You can specify a different port using the `--port` option: + +```bash +thv serve --port +``` + +If you're running the API server on a remote host, specify the hostname or IP +address to bind to using the `--host` option: + +```bash +thv serve --host +``` + +## UNIX socket support + +The API server can also be exposed via a UNIX socket instead of a TCP port. Use +the `--socket` option to specify a socket path: + +```bash +thv serve --socket /tmp/toolhive.sock +``` + +When using a UNIX socket, the `--socket` argument overrides the host:port +address configuration. + +## API documentation + +See the [ToolHive API documentation](../reference/api.mdx) for details on +available endpoints, request and response formats. + +You can also run a local instance of the API documentation using the `--openapi` +option: + +```bash +thv serve --openapi +``` + +Open a browser to `http://localhost:8080/api/doc` to view the API documentation. +The OpenAPI specification is also available at +`http://localhost:8080/api/openapi.json`. + +## Related information + +- [ToolHive API documentation](../reference/api.mdx) +- [`thv serve` command reference](../reference/cli/thv_serve.md) diff --git a/versioned_docs/version-1.0/toolhive/guides-cli/auth.mdx b/versioned_docs/version-1.0/toolhive/guides-cli/auth.mdx new file mode 100644 index 00000000..d487daee --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-cli/auth.mdx @@ -0,0 +1,156 @@ +--- +title: Authentication and authorization +description: + How to set up authentication and authorization for MCP servers using the + ToolHive CLI. +--- + +import OidcPrerequisites from '../_partials/_oidc-prerequisites.mdx'; +import BasicCedarConfig from '../_partials/_basic-cedar-config.mdx'; +import AuthTroubleshooting from '../_partials/_auth-troubleshooting.mdx'; + +This guide shows you how to secure your MCP servers using OAuth-based +authentication and Cedar-based authorization policies with the ToolHive CLI. + +:::info + +Authentication and authorization are emerging capabilities in the MCP ecosystem. +The official MCP authorization specification is still evolving, and client +support for these features is limited. ToolHive is leading the way in +implementing these capabilities, but you may encounter some limitations with +certain clients. + +::: + +## Prerequisites + + + +## Set up authentication + +### Step 1: Gather OIDC configuration + +First, collect the necessary information from your identity provider: + +- Client ID +- Audience value +- Issuer URL +- JWKS URL (for key verification) + +### Step 2: Run an MCP server with authentication + +Use the following command to start an MCP server with authentication enabled: + +```bash +thv run \ + --oidc-audience \ + --oidc-client-id \ + --oidc-issuer \ + --oidc-jwks-url \ + +``` + +Replace the placeholders with your actual OIDC configuration. + +### Step 3: Test authentication + +Once your server is running with authentication enabled, clients must include a +valid JWT (JSON Web Token) in the `Authorization` header of each HTTP request. +The token should: + +- Be issued by your configured identity provider +- Include the correct audience claim +- Not be expired +- Have a valid signature + +:::note[Client support limitations] + +Client support for authentication is limited at this time. While some clients +support HTTP headers with SSE MCP client configurations, you should not pass JWT +tokens in this way since it requires manual configuration and exposes your token +in plain text. + +ToolHive is working on a solution to securely handle authentication for clients. +Stay tuned for updates on this feature. + +::: + +:::note[Obtaining JWT tokens] + +How to obtain JWT tokens varies by identity provider and is outside the scope of +this guide. Consult your identity provider's documentation for specific +instructions on: + +- Interactive user authentication flows (OAuth 2.0 Authorization Code flow) +- Service-to-service authentication (Client Credentials flow) +- API token generation and management + +For Kubernetes service accounts, tokens are automatically mounted at +`/var/run/secrets/kubernetes.io/serviceaccount/token` in pods. + +::: + +To verify that authentication is working, you can use a tool like `curl` to make +a request to your MCP server: + +```bash +curl -H "Authorization: Bearer " \ + +``` + +## Set up authorization + +ToolHive uses Amazon's Cedar policy language for fine-grained, secure-by-default +authorization. Authorization is explicit: if a request is not explicitly +permitted by a policy, it is denied. Deny rules always take precedence over +permit rules. + +### Step 1: Create an authorization configuration file + + + +Save this file to a location accessible to ToolHive, such as +`/path/to/authz-config.json`. + +### Step 2: Run an MCP server with authorization + +Start your MCP server with the authorization configuration: + +```bash +thv run \ + --authz-config /path/to/authz-config.json \ + +``` + +You can combine this with the authentication parameters from the previous +section: + +```bash +thv run \ + --oidc-audience \ + --oidc-client-id \ + --oidc-issuer \ + --oidc-jwks-url \ + --authz-config /path/to/authz-config.json \ + +``` + +### Step 3: Test authorization + +Once your server is running with authorization enabled, clients will be subject +to the Cedar policies defined in your configuration file. When a client attempts +to perform an action, ToolHive will evaluate the request against the policies. +If the request is permitted, the action will proceed; otherwise, it will be +denied with a 403 Forbidden response. + +## Related information + +- For conceptual understanding, see + [Authentication and authorization framework](../concepts/auth-framework.mdx) +- For detailed Cedar policy syntax, see + [Cedar policies](../concepts/cedar-policies.mdx) and the + [Cedar documentation](https://docs.cedarpolicy.com/) + +## Troubleshooting + + diff --git a/versioned_docs/version-1.0/toolhive/guides-cli/build-containers.mdx b/versioned_docs/version-1.0/toolhive/guides-cli/build-containers.mdx new file mode 100644 index 00000000..fae9a1a4 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-cli/build-containers.mdx @@ -0,0 +1,501 @@ +--- +title: Build MCP containers +description: + How to build MCP server containers without running them using the ToolHive + CLI. +--- + +This guide explains how to use the [`thv build`](../reference/cli/thv_build.md) +command to build MCP server containers from protocol schemes without running +them. This is useful for pre-building containers for Kubernetes deployments, +CI/CD pipelines, and container registry workflows. + +## Overview + +The `thv build` command allows you to build containers from protocol schemes +(`uvx://`, `npx://`, `go://`) without immediately running them. This provides +several benefits: + +- **Pre-build containers** for faster deployment in Kubernetes environments +- **Separate build and run phases** in CI/CD pipelines +- **Custom image tagging** for container registry workflows +- **Dockerfile generation** for inspection and customization +- **Build validation** before deployment + +## Basic usage + +To build a container from a protocol scheme: + +```bash +thv build +``` + +For example: + +```bash +# Build a Python MCP server using uvx +thv build uvx://mcp-server-git + +# Build a Node.js MCP server using npx with a pinned version +thv build npx://@upstash/context7-mcp@1.0.26 + +# Build a Go MCP server +thv build go://github.com/example/my-mcp-server@latest +``` + +:::info[What's happening?] + +When you run `thv build`, ToolHive: + +1. Detects the protocol scheme and extracts the package reference +2. Generates a Dockerfile based on the appropriate template +3. Builds a Docker image with the package installed +4. Tags the image with an auto-generated name or your custom tag +5. Displays the built image name for use with other tools + +::: + +## Custom image tagging + +Use the `--tag` (or `-t`) flag to specify a custom name and tag for the built +image: + +```bash +thv build --tag my-custom-name:latest npx://@modelcontextprotocol/server-filesystem +``` + +This is particularly useful for: + +- **Container registries**: Tag images for pushing to registries +- **Kubernetes deployments**: Use predictable image names in manifests +- **Version management**: Tag images with specific versions + +### Tagging examples + + + + +Build and tag for pushing to a container registry: + +```bash +# Build and tag for Docker Hub +thv build --tag myusername/mcp-git-server:v1.0.0 uvx://mcp-server-git + +# Build and tag for GitHub Container Registry +thv build --tag ghcr.io/myorg/mcp-filesystem:latest npx://@modelcontextprotocol/server-filesystem + +# Push to registry +docker push ghcr.io/myorg/mcp-filesystem:latest +``` + + + + +Build images with predictable names for Kubernetes manifests: + +```bash +# Build with a consistent tag +thv build --tag mcp-servers/git-server:stable uvx://mcp-server-git +``` + +Use the built image in your Kubernetes manifests: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: git-server + namespace: production +spec: + image: mcp-servers/git-server:stable + # ... other spec fields ... +``` + + + + +Build multiple versions of the same server: + +```bash +# Build different versions +thv build --tag mcp-git:v1.0.0 uvx://mcp-server-git@1.0.0 +thv build --tag mcp-git:v1.1.0 uvx://mcp-server-git@1.1.0 +thv build --tag mcp-git:latest uvx://mcp-server-git@latest +``` + + + + +## Protocol schemes + +The `thv build` command supports the same protocol schemes as +[`thv run`](./run-mcp-servers.mdx#run-a-server-using-protocol-schemes): + +### Python (uvx) + +Build Python-based MCP servers using the uv package manager: + +```bash +# Build with auto-generated name +thv build uvx://mcp-server-git + +# Build with custom tag +thv build --tag my-git-server:latest uvx://mcp-server-git@1.2.0 +``` + +### Node.js (npx) + +Build Node.js-based MCP servers using npm: + +```bash +# Build with auto-generated name +thv build npx://@modelcontextprotocol/server-filesystem + +# Build with custom tag +thv build --tag filesystem-server:v2.0 npx://@modelcontextprotocol/server-filesystem@2.0.0 +``` + +### Go + +Build Go-based MCP servers: + +```bash +# Build from remote Go module +thv build --tag grafana-mcp:latest go://github.com/grafana/mcp-grafana/cmd/mcp-grafana@latest + +# Build from local Go project +thv build --tag my-local-server:dev go://./cmd/my-mcp-server +``` + +## Build-time arguments + +Some MCP servers require specific subcommands or arguments that must always be +present. You can bake these required arguments directly into the container image +at build time using the `--` separator at the end of the `thv build` command: + +```bash +thv build -- +``` + +:::info[Build-time vs runtime arguments] + +- **Build-time arguments**: Baked into the container image and always present. + These are typically required subcommands or essential configuration flags. +- **Runtime arguments**: Passed when running the container and appended after + build-time arguments. These are typically optional flags or dynamic + configuration. + +::: + +Build-time arguments are embedded in the container's ENTRYPOINT and always +execute before any runtime arguments. For example, the LaunchDarkly MCP server +requires a `start` subcommand: + +```bash +# Bake "start" subcommand into container +thv build --tag launchdarkly-mcp:latest npx://@launchdarkly/mcp-server -- start + +# Runtime args still append after baked-in args +thv run launchdarkly-mcp:latest -- --verbose +# Executes: npx @launchdarkly/mcp-server start --verbose +``` + +You can include multiple build-time arguments as needed: + +```bash +thv build uvx://my-package -- --transport stdio --log-level info +``` + +## Dockerfile generation + +Use the `--dry-run` flag to generate the Dockerfile without building the image: + +```bash +# Output Dockerfile to stdout +thv build --dry-run uvx://mcp-server-git + +# Save Dockerfile to a file +thv build --dry-run --output Dockerfile.mcp-git uvx://mcp-server-git +``` + +This is useful for: + +- **Inspecting the build process** before building +- **Customizing Dockerfiles** for specific requirements +- **Understanding dependencies** and build steps +- **Debugging build issues** + +### Example Dockerfile output + +```dockerfile +# Generated by: thv build --dry-run uvx://mcp-server-git +FROM python:3.12-slim + +# Install uv +RUN pip install uv + +# Install the package +RUN uv tool install mcp-server-git + +# Set the entrypoint +ENTRYPOINT ["uv", "tool", "run", "mcp-server-git"] +``` + +## Kubernetes workflows + +The `thv build` command is especially useful for Kubernetes deployments where +you need to pre-build containers before deploying them. + +### Pre-build workflow + +1. **Build the container** with a specific tag: + + ```bash + thv build --tag ghcr.io/myorg/mcp-git:v1.0.0 uvx://mcp-server-git@1.0.0 + ``` + +2. **Push to container registry**: + + ```bash + docker push ghcr.io/myorg/mcp-git:v1.0.0 + ``` + +3. **Deploy to Kubernetes** using the pre-built image: + + ```yaml + apiVersion: toolhive.stacklok.dev/v1alpha1 + kind: MCPServer + metadata: + name: git-server + namespace: production + spec: + image: ghcr.io/myorg/mcp-git:v1.0.0 + transport: stdio + ``` + +### CI/CD integration + +Integrate `thv build` into your CI/CD pipeline for automated container building: + +```yaml +# Example GitHub Actions workflow +name: Build and Deploy MCP Server +on: + push: + tags: ['v*'] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install ToolHive + uses: StacklokLabs/toolhive-actions/install@v0 + with: + version: latest + + - name: Log in to Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build MCP server + run: | + thv build --tag ghcr.io/${{ github.repository }}/mcp-server:${{ github.ref_name }} \ + uvx://mcp-server-git@${{ github.ref_name }} + + - name: Push to registry + run: | + docker push ghcr.io/${{ github.repository }}/mcp-server:${{ github.ref_name }} +``` + +For more advanced CI/CD patterns including multi-architecture builds, supply +chain security, and change detection, see the +[Advanced CI/CD patterns](./advanced-cicd.mdx) guide. + +## Advanced usage + +### Build with custom CA certificates + +For corporate environments with custom certificate authorities: + +```bash +# Use global CA certificate configuration +thv config set-ca-cert /path/to/corporate-ca.crt +thv build uvx://internal-mcp-server + +# Override CA certificate for specific build +thv build --ca-cert /path/to/special-ca.crt uvx://special-server +``` + +### Custom package registries + +Enterprise environments often use private package registries or mirrors instead +of public registries like npm or PyPI. ToolHive supports configuring build +environment variables that are injected into the Dockerfile during builds, +allowing you to use custom registries for all protocol scheme builds. + +#### Set build environment variables + +Use the `thv config set-build-env` command to configure environment variables +that will be included in all builds: + +```bash +thv config set-build-env +``` + +Common environment variables for package registries: + +| Package manager | Environment variable | Example value | +| --------------- | --------------------- | -------------------------------------- | +| npm | `NPM_CONFIG_REGISTRY` | `https://npm.corp.example.com` | +| Go | `GOPROXY` | `https://goproxy.corp.example.com` | +| Go | `GOPRIVATE` | `github.com/mycompany/*` | +| pip/uv | `PIP_INDEX_URL` | `https://pypi.corp.example.com/simple` | +| pip/uv | `PIP_TRUSTED_HOST` | `pypi.corp.example.com` | + +Example configuration for an enterprise environment: + +```bash +# Configure npm to use a corporate registry +thv config set-build-env NPM_CONFIG_REGISTRY https://npm.corp.example.com + +# Configure Go proxy for private modules +thv config set-build-env GOPROXY https://goproxy.corp.example.com +thv config set-build-env GOPRIVATE "github.com/mycompany/*" + +# Configure Python/uv to use a corporate PyPI mirror +thv config set-build-env PIP_INDEX_URL https://pypi.corp.example.com/simple +``` + +### Build local Go projects + +Build MCP servers from local Go projects: + +```bash +# Build from current directory +cd my-go-mcp-project +thv build --tag my-server:dev go://. + +# Build from relative path +thv build --tag my-server:dev go://./cmd/server + +# Build from absolute path +thv build --tag my-server:dev go:///path/to/my-project +``` + +## Comparison with thv run + +| Feature | `thv build` | `thv run` | +| ------------------------- | ------------------------ | ------------------------ | +| **Purpose** | Build containers only | Build and run containers | +| **Output** | Container image | Running MCP server | +| **Use case** | Pre-building, CI/CD | Development, testing | +| **Kubernetes** | Pre-build for deployment | Direct development | +| **Custom tagging** | ✅ `--tag` flag | ❌ Auto-generated names | +| **Dockerfile generation** | ✅ `--dry-run` flag | ❌ Not available | + +## Next steps + +- Use built containers with [`thv run`](./run-mcp-servers.mdx) for local + development +- Deploy pre-built containers to [Kubernetes](../guides-k8s/run-mcp-k8s.mdx) +- Set up [CI/CD pipelines](#cicd-integration) for automated building +- Learn about [container registry workflows](#custom-image-tagging) + +## Related information + +- [`thv build` command reference](../reference/cli/thv_build.md) +- [Run MCP servers](./run-mcp-servers.mdx) +- [Run MCP servers in Kubernetes](../guides-k8s/run-mcp-k8s.mdx) +- [Custom permissions](./custom-permissions.mdx) + +## Troubleshooting + +
+Build fails with network errors + +If builds fail with network connectivity issues: + +1. **Check internet connectivity** for downloading packages +2. **Configure CA certificates** for corporate environments: + + ```bash + thv config set-ca-cert /path/to/corporate-ca.crt + ``` + +3. **Use proxy settings** if required by your network +4. **Verify package names** and versions exist in the respective registries + +
+ +
+Invalid image tag format + +If you get image tag validation errors: + +1. **Use valid Docker image tag format**: `name:tag` or `registry/name:tag` +2. **Avoid special characters** except hyphens, underscores, and dots +3. **Use lowercase names** for compatibility +4. **Check tag length limits** (typically 128 characters) + +Example valid tags: + +```bash +thv build --tag my-server:latest uvx://package +thv build --tag ghcr.io/org/server:v1.0.0 npx://package +``` + +
+ +
+Package not found errors + +If the build fails because a package cannot be found: + +1. **Verify package exists** in the respective registry: + - Python: Check [PyPI](https://pypi.org/) + - Node.js: Check [npm](https://www.npmjs.com/) + - Go: Verify the module path and version + +2. **Check version specifiers**: + + ```bash + # Correct version formats + thv build uvx://package@1.0.0 + thv build npx://package@latest + thv build go://github.com/user/repo@v1.0.0 + ``` + +3. **For Go modules**, ensure the path includes the correct import path + +
+ +
+Custom registry not being used + +If your custom package registry configuration isn't being applied: + +1. **Verify your build environment configuration**: + + ```bash + thv config get-build-env + ``` + +2. **Check the environment variable names** match what your package manager + expects (for example, `NPM_CONFIG_REGISTRY` for npm, `GOPROXY` for Go) + +3. **Use `--dry-run` to inspect the generated Dockerfile** and verify your + environment variables are being injected: + + ```bash + thv build --dry-run uvx://my-package + ``` + +4. **Ensure network connectivity** to your custom registry from inside the + container build environment + +
diff --git a/versioned_docs/version-1.0/toolhive/guides-cli/client-configuration.mdx b/versioned_docs/version-1.0/toolhive/guides-cli/client-configuration.mdx new file mode 100644 index 00000000..0bb06915 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-cli/client-configuration.mdx @@ -0,0 +1,279 @@ +--- +title: Client configuration +description: How to configure AI agent clients to work with ToolHive MCP servers. +--- + +import ClientIntro from '../_partials/_client-config-intro.mdx'; + + + +## Register clients + +The easiest way to register clients is to run the +[setup command](../reference/cli/thv_client_setup.md): + +```bash +thv client setup +``` + +ToolHive discovers supported clients installed on your system and lets you +select which ones to register. ToolHive detects clients based on the presence of +the client's configuration file in its default location. See the +[Client compatibility reference](../reference/client-compatibility.mdx) for +details on which clients ToolHive supports and how it detects them. + +To view the current status of detected and configured clients, run: + +```bash +thv client status +``` + +:::note + +ToolHive previously included an "auto-discovery" mode. We removed this mode in +v0.1.0 to simplify client configuration and ensure consistent control and +behavior. If you previously enabled client auto-discovery, ToolHive will +explicitly register all detected clients the first time you run v0.1.0 and +higher. + +Going forward, use the `thv client setup` command to manage your client +configurations. + +::: + +## Alternative client registration + +If you prefer to register clients manually or in an automated script, use the +`thv client register` command: + +```bash +thv client register +``` + +Replace `` with the name of your client. Common client names +include: + +- `claude-code` - Claude Code CLI +- `cursor` - Cursor IDE +- `roo-code` - Roo Code extension for Visual Studio Code +- `cline` - Cline extension for Visual Studio Code +- `vscode` - Visual Studio Code (GitHub Copilot) +- `vscode-insider` - VS Code Insiders edition + +Example: + +```bash +thv client register vscode +``` + +You can register a client with a specific group using the `--group` option: + +```bash +thv client register --group +``` + +Run [`thv client register --help`](../reference/cli/thv_client_register.md) for +the latest list of supported clients. + +To list currently registered clients: + +```bash +thv client list-registered +``` + +Repeat the registration step for any additional clients you want to configure. + +You might need to restart your client application for the configuration to take +effect. + +To remove a client configuration: + +```bash +thv client remove +``` + +To remove a client configuration from a specific group: + +```bash +thv client remove --group +``` + +## Other clients or tools + +If you have other clients or development libraries that ToolHive doesn't +directly support, you can still configure them to use ToolHive-managed MCP +servers if they support the SSE or Streamable HTTP protocol. Check your client +or library documentation for configuration details. + +List your running MCP servers to get the URL: + +```bash +thv list +``` + +Example output (some fields omitted for brevity): + +```text +NAME PACKAGE STATUS URL PORT +github ghcr.io/github/github-mcp-server:latest running http://127.0.0.1:55264/mcp 55264 +sqlite ghcr.io/stackloklabs/sqlite-mcp/server:latest running http://127.0.0.1:22089/sse#sqlite 22089 +``` + +In this example, the `github` server uses the Streamable HTTP transport (URL +ends in `/mcp`), and the `sqlite` server uses SSE (URL ends in `/sse`). + +You can also get the list in JSON format, which works with many clients that use +the standard configuration format: + +```bash +thv list --format mcpservers +``` + +Example output: + +```json +{ + "mcpServers": { + "github": { + "url": "http://127.0.0.1:55264/mcp" + }, + "sqlite": { + "url": "http://127.0.0.1:22089/sse#sqlite" + } + } +} +``` + +Configure your client or library to connect to the MCP server using the URL +ToolHive provides. + +## Related information + +- [`thv client` command reference](../reference/cli/thv_client.md) +- [`thv config` command reference](../reference/cli/thv_config.md) +- [Client compatibility](../reference/client-compatibility.mdx) +- [Run MCP servers](./run-mcp-servers.mdx) + +## Troubleshooting + +
+Client is not detected by `thv client setup` + +If ToolHive doesn't detect your client: + +1. Verify ToolHive supports your client in the + [Client compatibility reference](../reference/client-compatibility.mdx). + +2. Make sure you installed the client in its default location. ToolHive detects + clients based on their configuration files. If the client isn't in its + default location, ToolHive can't detect it. + +3. Try manually registering the client: + + ```bash + thv client register + ``` + +
+ +
+Client can't connect to MCP server + +If your client can't connect to the MCP server: + +1. Verify the MCP server is running: + + ```bash + thv list + ``` + + See [Test MCP servers](./test-mcp-servers.mdx) for help testing the MCP + server's availability. + +2. Check if the client is registered: + + ```bash + thv client status + ``` + +3. Restart your client application. + +
+ +
+Client can connect but tools aren't available + +If your client connects to the MCP server but tools aren't available: + +1. Make sure the MCP server is running and accessible: + + ```bash + thv list + ``` + + See [Test MCP servers](./test-mcp-servers.mdx) for help testing the MCP + server's functionality. + +2. Check the MCP server logs: + + ```bash + thv logs + ``` + +3. Make sure you properly configured the MCP server in your client. +4. If the MCP server requires authentication or has authorization policies + applied, make sure the client has the necessary permissions to access the + tools. + +
+ +
+Containerized client can't connect to MCP server + +When your MCP client runs in a container (like containerized LibreChat), it +can't reach the ToolHive proxy on your host machine using `localhost` due to +container network isolation. The ToolHive proxy runs as an OS process on the +host and provides access to containerized MCP servers. + +Configure your containerized client to use the appropriate host address for your +platform: + +- **Docker Desktop (macOS/Windows)**: `host.docker.internal` +- **Podman Desktop**: `host.containers.internal` +- **Docker Engine (Linux)**: `172.17.0.1` (or your custom bridge gateway IP) + +For example, change the MCP server URL from `http://localhost:8080/mcp` to +`http://host.docker.internal:8080/mcp` in your client configuration. + +
+ +
+VS Code can't connect to some streamable-http servers + +You might encounter errors with Visual Studio Code connecting to some +Python-based MCP servers using the Streamable HTTP transport protocol: + +```text +[info] Connection state: Error Error sending message to http://localhost:49574/mcp: TypeError: fetch failed +[error] Server exited before responding to `initialize` request. +``` + +This is a known interaction between VS Code and certain versions of the FastMCP +SDK used by Python-based MCP servers. If you inspect the HTTP connection, you'll +see a `307 Temporary Redirect` response, which VS Code doesn't handle correctly. + +This +[issue is resolved](https://github.com/modelcontextprotocol/python-sdk/pull/781) +in the latest versions of the SDK, but if you're using an older version of a +Python-based MCP server, you might still encounter it. + +There are two workarounds: + +1. Change the URL in your VS Code settings to add a trailing slash to the MCP + server URL. For example, change `http://localhost:49574/mcp` to + `http://localhost:49574/mcp/`. You'll need to re-apply this if you stop and + restart the MCP server. +2. If the MCP server supports SSE, switch to using the SSE transport instead of + Streamable HTTP. + +
diff --git a/versioned_docs/version-1.0/toolhive/guides-cli/custom-permissions.mdx b/versioned_docs/version-1.0/toolhive/guides-cli/custom-permissions.mdx new file mode 100644 index 00000000..2fa313fa --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-cli/custom-permissions.mdx @@ -0,0 +1,237 @@ +--- +title: Custom permissions +description: + How to create and apply file system permissions and network isolation for MCP + servers using permission profiles in ToolHive. +--- + +ToolHive includes a permission system that lets you control an MCP server's +access to your host's file system and network resources. This helps you maintain +security and ensures that MCP servers operate within defined boundaries. + +## Understanding permission profiles + +Permissions are defined using _permission profiles_—JSON files that specify both +file system and network access rules for an MCP server. Each MCP server can use +only one permission profile at a time, so you include all necessary permissions +in a single profile. + +Permission profiles control two types of access: + +- **Host file system access** specifies paths on your host that are mounted into + the MCP server container — see [file system access](./filesystem-access.mdx) + for detailed examples +- **Network access rules** let you restrict outbound HTTP(S) connectivity from + the MCP server — see [network isolation](./network-isolation.mdx) for + architecture details and examples + +### Profile structure + +Profiles are defined in JSON format and can include the following properties: + +- `read`: List of paths on your host file system that the MCP server can read +- `write`: List of file system paths that the MCP server can write to (this also + implies read access) +- `network`: Network access rules for inbound and outbound connections: + - `inbound`: Inbound network access rules, which include: + - `allow_host`: List of allowed hostnames that can send traffic to the MCP + server. If not specified, only the container's own hostname, `localhost`, + and `127.0.0.1` are allowed. + - `outbound`: Outbound network access rules, which include: + - `insecure_allow_all`: If set to `true`, allows unrestricted outbound + network access. This isn't recommended for production use. + - `allow_host`: List of allowed hostnames or IP addresses for outbound + connections. To allow all subdomains of a domain, prefix the domain with a + period (e.g., `.github.com` allows any subdomain of `github.com`). + Wildcards are not supported. + - `allow_port`: List of allowed ports for outbound connections + +## Default permissions in the ToolHive registry + +ToolHive includes default least-privilege permissions for MCP servers in the +built-in registry. These defaults balance functionality and security, but you +should review them to make sure they meet your specific requirements. + +View these permissions using the following command: + +```bash +thv registry info +``` + +In the output, look for the "Permissions" section: + +```test +Permissions: + Network: + Allow Host: .google.com + Allow Port: 443 +``` + +This example shows that the MCP server can make outbound network connections to +`*.google.com` (note the leading `.` which enables subdomain matching) on +port 443. + +:::info + +For security reasons, none of the MCP servers in the registry have any default +file system permissions defined. This means they cannot read or write to any +paths on your host system unless you explicitly grant access using a custom +permission profile or use the `--volume` flag when running the server. + +::: + +Always verify the default permissions and override them with a custom profile if +needed to meet your security policies. + +:::tip + +Add `--format json` to the +[`thv registry info`](../reference/cli/thv_registry_info.md) command to get the +output in JSON format for easier customization. Use the contents of the +`permissions` section as a starting point for creating a custom profile. + +::: + +## Built-in profiles + +ToolHive includes two built-in profiles for network access that you can use +without creating a custom file: + +- **`network`**: Permits all outbound network access. This is the default + profile applied to MCP servers when you run a custom server without the + `--permission-profile` flag. + + :::info[Important] + + This profile is useful for development and testing but isn't recommended for + production use since it doesn't restrict network destinations. Create a custom + profile that specifies the allowed hosts and ports when possible. + + ::: + +- **`none`**: Restricts all network access. Use this for MCP servers that don't + require external connectivity. + +Both built-in profiles provide no file system access by default. To add file +system permissions, either: + +- Use the `--volume` flag to mount specific paths +- Create a custom profile that includes both network settings and file system + permissions + +## Create a custom permission profile + +You can create a JSON file with your desired permissions. Include file system +permissions, network permissions, or both in the same profile. + +### Example: Combined file system and network permissions + +When your MCP server needs both file access and network connectivity, include +both types of permissions in a single profile: + +```json title="~/my-server-profile.json" +{ + "read": ["/home/user/documents", "/home/user/config"], + "write": ["/home/user/output"], + "network": { + "outbound": { + "insecure_allow_all": false, + "allow_host": ["api.github.com", ".googleapis.com"], + "allow_port": [443] + } + } +} +``` + +This profile: + +- Mounts `/home/user/documents` and `/home/user/config` as read-only paths +- Mounts `/home/user/output` with read and write access (note that `write` also + implies read access) +- Allows outbound HTTPS connections to `api.github.com` and any subdomain of + `googleapis.com` + +### Example: Network-only permissions + +Use this approach when your MCP server needs to make API calls but doesn't +require file system access: + +```json title="~/network-only-profile.json" +{ + "network": { + "outbound": { + "allow_host": ["host.docker.internal", ".intranet.example.com"], + "allow_port": [8080, 3000] + } + } +} +``` + +This profile allows the server to connect to local development servers and +internal company resources without granting any file system access. + +See [network isolation](./network-isolation.mdx) for more details about network +permissions and how isolation works. + +### Example: File system-only permissions + +Use this when your MCP server works with local files but doesn't need network +access: + +```json title="~/filesystem-only-profile.json" +{ + "read": ["/var/log"], + "write": ["/tmp/mcp-output"] +} +``` + +This profile grants file system access without defining any network permissions. +(Note, to actually block network access, use the `--isolate-network` flag when +running the server.) + +See [file system access](./filesystem-access.mdx) for more details and specific +examples. + +## Apply a permission profile + +### Using a built-in profile + +To run an MCP server with unrestricted network access (the default): + +```bash +thv run --permission-profile network +``` + +To run an MCP server with no network access: + +```bash +thv run --isolate-network --permission-profile none +``` + +### Using a custom profile file + +To run an MCP server with your custom profile: + +```bash +thv run --isolate-network --permission-profile /path/to/custom-profile.json +``` + +## Security best practices + +When creating and using permission profiles: + +- Use the `none` profile when possible for MCP servers that don't require + network or file access +- Only grant necessary permissions +- Avoid enabling `network.outbound.insecure_allow_all`, as this allows + unrestricted outbound network access +- Review and test custom profiles thoroughly +- Keep permission profiles in version control to track changes and share them + with your team + +## Related information + +- [`thv run` command reference](../reference/cli/thv_run.md) +- [Run MCP servers](./run-mcp-servers.mdx) +- [File system access](./filesystem-access.mdx) +- [Network isolation](./network-isolation.mdx) diff --git a/versioned_docs/version-1.0/toolhive/guides-cli/filesystem-access.mdx b/versioned_docs/version-1.0/toolhive/guides-cli/filesystem-access.mdx new file mode 100644 index 00000000..7e6af4d9 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-cli/filesystem-access.mdx @@ -0,0 +1,248 @@ +--- +title: File system access +description: How to enable MCP server access to your host's file system in ToolHive. +--- + +Since ToolHive runs MCP servers in isolated containers, they don't have access +to your host's file system by default. However, some servers need to read or +write files on the host system to function properly or persist data. + +There are two ways to enable file system access for MCP servers in ToolHive: + +1. **The `--volume` flag**: Mount files using Docker volume syntax. + +2. **Permission profiles**: Create a custom permission profile that defines + which host paths the MCP server can read or write. + +## Choose a method + +When deciding how to enable file system access for an MCP server, consider the +differences between permission profiles and the `volume` flag: + +- If you use a permission profile, ToolHive mounts the paths you specify in the + `read` and `write` properties to the same location inside the container. The + MCP server will see files at the same path as on your host. If your server + expects files in a different location, it might not work properly. + +- With the `--volume` flag, you use Docker's syntax to set both the source path + on your host and the destination path inside the container. This gives you + more control over where files appear in the MCP server's environment. Use this + when your server needs files in a specific location. + +Choose the method that matches your server's requirements. Use permission +profiles for simple, direct mappings. Use the `--volume` flag when you need to +customize the destination path inside the container. + +## The `--volume` flag + +To enable file system access using the `--volume` flag, you specify the host +path and the container path in the `thv run` command. The syntax is the same as +Docker's volume syntax for bind mounts: + +```bash +thv run --volume :[:ro] +``` + +You can add the `--volume` flag multiple times to mount multiple paths. + +The optional `:ro` suffix makes the volume read-only in the container. This is +useful for sharing files without letting the MCP server modify them. For +example, to mount a host directory `/home/user/data` as read-only in the +container at `/data`, run: + +```bash +thv run --volume /home/user/data:/data:ro +``` + +To mount a host file `/home/user/config.json` as read-write in the container at +`/app/config.json`, run: + +```bash +thv run --volume /home/user/config.json:/app/config.json +``` + +### Use with built-in network profiles + +You can also use the `--volume` flag with built-in network profiles for a +flexible approach that doesn't require creating a custom profile file. + +For example, the AWS Diagram MCP server doesn't need any network connectivity, +but generates diagrams locally. You can use the built-in `none` profile to block +all network access and mount a directory for the generated diagrams: + +```bash +thv run --isolate-network --permission-profile none --volume /home/user/aws-diagrams:/tmp/generated-diagrams aws-diagram +``` + +This approach is useful when you need simple file system access alongside +standard network restrictions. + +## Permission profiles + +To enable file system access using a permission profile, create a custom profile +that specifies which host paths the MCP server can read or write. You can +include file system permissions alone or pair them with network permissions in +the same profile. + +:::note + +Paths in permission profiles must be absolute paths on your host system. +Relative paths and expanded paths (like `~/`) are not supported. Always use full +paths starting from the root directory (e.g., `/home/user/documents`). + +::: + +### File system-only profile + +Create a JSON file with your desired file system permissions: + +```json title="file-permissions.json" +{ + "read": ["/home/user/documents", "/var/log/app"], + "write": ["/home/user/output"] +} +``` + +This profile: + +- Mounts `/home/user/documents` and `/var/log/app` as read-only paths +- Mounts `/home/user/output` as a read-write path (note that `write` also + implies read access) + +### Paired with network permissions + +You can also pair file system permissions with network access in the same +profile: + +```json title="combined-permissions.json" +{ + "read": ["/home/user/config"], + "write": ["/home/user/data"], + "network": { + "outbound": { + "allow_host": ["api.example.com"], + "allow_port": [443] + } + } +} +``` + +### Apply the profile + +To run an MCP server with your profile: + +```bash +thv run --permission-profile ./file-permissions.json +``` + +For more details on permission profiles and network permissions, see +[custom permissions](./custom-permissions.mdx). + +## Examples + +Below are some examples of how to use file system access with some common MCP +servers in the ToolHive registry. + +:::tip + +The example MCP servers below don't require network access, and they have +restricted permission profiles in the ToolHive registry. To further secure these +examples, add the `--isolate-network` flag to block network access entirely. +This ensures the MCP server can only access the file system as specified in your +profile or volume mounts. + +::: + +### Filesystem MCP server + +The +[Filesystem MCP server](https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem) +lets you read and write files on your host system. By default, it expects paths +to be mounted under `/projects` inside the container. + +Here's how to mount two directories using the `--volume` flag, with one as +read-only: + +```bash +thv run --volume ~/documents:/projects/docs:ro --volume ~/code:/projects/code filesystem +``` + +You can achieve the same result using a permission profile. Create a file called +`filesystem-profile.json`: + +```json title="filesystem-profile.json" +{ + "read": ["/home/user/documents"], + "write": ["/home/user/code"] +} +``` + +Since permission profiles mount paths at the same location inside the container +as they exist on your host, you need to tell the filesystem server which base +directory to allow. Pass `/home/user` as an argument: + +```bash +thv run --permission-profile ./filesystem-profile.json filesystem -- /home/user +``` + +### Memory MCP server + +The +[Memory MCP server](https://github.com/modelcontextprotocol/servers/tree/main/src/memory) +uses a local knowledge graph to persist information across chats. + +To preserve data between runs, you need to mount a file. First, create an empty +JSON file, then mount it to the expected location inside the container: + +```bash +touch ./memory.json +thv run --volume ./memory.json:/app/dist/memory.json memory +``` + +### SQLite MCP server + +The [SQLite MCP server](https://github.com/StacklokLabs/sqlite-mcp) works with +database files on your host system. By default, it expects to find a database +file at `/database.db` inside the container: + +```bash +thv run --volume ~/my-database.db:/database.db sqlite +``` + +If you want to place the database file in a different location inside the +container, use the server's `--db` flag to specify the new path: + +```bash +thv run --volume ~/my-database.db:/data/my-database.db sqlite -- --db /data/my-database.db +``` + +## Related information + +- [`thv run` command reference](../reference/cli/thv_run.md) +- [Run MCP servers](./run-mcp-servers.mdx) +- [Custom permissions](./custom-permissions.mdx) +- [Network isolation](./network-isolation.mdx) + +## Troubleshooting + +
+File system access issues + +If your MCP server can't access the file system as expected: + +1. Verify that the paths in your profile or volume flag are correct +2. Ensure the host paths exist and have the correct permissions + - The MCP server runs as a specific user inside the container, so the host + paths must be accessible to that user +3. Check that the permissions are set correctly (read/write) +4. Inspect the container's mounted paths to ensure they match your expectations: + + ```bash + docker inspect + ``` + + Look for the `Mounts` section to see how paths are mapped. + +5. Restart the server with the updated profile or corrected volume mount + +
diff --git a/versioned_docs/version-1.0/toolhive/guides-cli/group-management.mdx b/versioned_docs/version-1.0/toolhive/guides-cli/group-management.mdx new file mode 100644 index 00000000..4370aae9 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-cli/group-management.mdx @@ -0,0 +1,127 @@ +--- +title: Organize servers into groups +description: How to organize MCP servers into logical groups and configure client access. +--- + +This guide explains how to organize your MCP servers into logical groups and +configure which groups your MCP clients can access. + +:::tip[New to groups?] + +If you're not sure whether groups are right for your use case, see +[Organizing MCP servers with groups](../concepts/groups.mdx) for an overview of +when and why to use them. + +::: + +:::info[What's the default behavior?] + +All MCP servers are automatically assigned to the `default` group unless you +specify otherwise. MCP clients configured without a specific group can access +all servers in the `default` group. + +::: + +## Create and organize groups + +### Create a group + +```bash +thv group create +``` + +For example, to create separate groups for different environments: + +```bash +thv group create development +thv group create production +``` + +### Run servers in a group + +When running an MCP server, specify the group using the `--group` flag: + +```bash +thv run --group development fetch +thv run --group production filesystem --volume /prod/repo:/projects:ro +``` + +:::info[What's happening?] + +When you specify a group: + +1. The server is assigned to that group and labeled accordingly +2. The server can only be accessed by clients configured for that group + +::: + +A single workload can only belong to one group at a time. To run multiple +instances of the same MCP server in different groups, use a unique name for each +instance: + +```bash +thv run --group development --name fetch-dev fetch +thv run --group production --name fetch-prod fetch +``` + +## Configure client access to groups + +You can configure MCP clients to access specific groups, giving you control over +which tools are available in different contexts. + +### Configure a client for a specific group + +When registering a client, you can specify which group it should access: + +```bash +thv client register --group development +``` + +This configures the client to only access servers in the `development` group. + +## Example workflows + +### Project-based organization + +```bash +# Create groups for different projects +thv group create webapp-frontend +thv group create webapp-backend + +# Run project-specific servers +thv run --group webapp-frontend mcp-react-tools +thv run --group webapp-backend mcp-database-tools + +# Configure clients with appropriate access +thv client register --client vscode --group webapp-frontend +thv client register --client cursor --group webapp-backend +``` + +## Manage groups + +### Remove a group + +Remove a group and move its servers to the default group: + +```bash +thv group rm development +``` + +Remove a group and delete all its servers: + +```bash +thv group rm development --with-workloads +``` + +:::warning + +Using `--with-workloads` permanently deletes all servers in the group. + +::: + +## Related information + +- [Client configuration](client-configuration.mdx) +- [Run MCP servers](run-mcp-servers.mdx) +- [`thv group` command reference](../reference/cli/thv_group.md) +- [`thv client` command reference](../reference/cli/thv_client.md) diff --git a/versioned_docs/version-1.0/toolhive/guides-cli/index.mdx b/versioned_docs/version-1.0/toolhive/guides-cli/index.mdx new file mode 100644 index 00000000..cd1057cd --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-cli/index.mdx @@ -0,0 +1,31 @@ +--- +title: Using the ToolHive CLI +description: + How-to guides for using the ToolHive command-line interface to run and manage + MCP servers. +--- + +import DocCardList from '@theme/DocCardList'; + +## Introduction + +The ToolHive CLI (`thv`) is a command-line tool that allows you to deploy and +manage MCP servers on your local machine or in development environments. It +provides quick deployment of MCP servers and supports advanced features like +custom permissions, network access filtering, and telemetry. + +It's designed for developers who prefer working in a terminal or need to +integrate MCP server management into scripts or automation workflows. + +:::info[Using ToolHive UI?] + +ToolHive UI includes and manages the CLI automatically. You don't need to +install the CLI separately. See +[CLI conflict resolution](./install.mdx#cli-conflict-resolution) if you have +both installed. + +::: + +## Contents + + diff --git a/versioned_docs/version-1.0/toolhive/guides-cli/install.mdx b/versioned_docs/version-1.0/toolhive/guides-cli/install.mdx new file mode 100644 index 00000000..95d0ac22 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-cli/install.mdx @@ -0,0 +1,473 @@ +--- +title: Install ToolHive +description: How to install, upgrade, and manage the ToolHive CLI. +--- + +This guide walks you through installing, upgrading, and managing the ToolHive +CLI ([`thv`](../reference/cli/thv.md)). + +![Latest version](https://img.shields.io/github/v/release/stacklok/toolhive?style=for-the-badge&label=Latest%20version&color=bddfc2) +![Release date](https://img.shields.io/github/release-date/stacklok/toolhive?display_date=published_at&style=for-the-badge&label=Released&color=bddfc2) + +## Prerequisites + +Before installing ToolHive, make sure your system meets these requirements: + +- **Operating systems**: macOS, Linux, or Windows +- **Container runtime**: + - Docker / Docker Desktop + - Podman / Podman Desktop + - Colima with Docker runtime + - Rancher Desktop with the dockerd/moby runtime (experimental) + +ToolHive requires minimal CPU, memory, and disk space. The exact requirements +depend on how many MCP servers you run and the resources they use. + +## Install ToolHive + +You can install ToolHive using several methods: + + + + +Homebrew is the easiest installation method on macOS or Linux: + +```bash +brew tap stacklok/tap +brew install thv +``` + + + + +WinGet is available by default on Windows, making this the easiest installation +method: + +```bash +winget install stacklok.thv +``` + +Open a new terminal window after installation to ensure the `thv` command is +available. + + + + +1. Visit the + [ToolHive releases page](https://github.com/stacklok/toolhive/releases/latest) + +2. Download the appropriate binary for your platform: + - `toolhive__darwin_amd64.tar.gz` for macOS (Intel) + - `toolhive__darwin_arm64.tar.gz` for macOS (Apple Silicon) + - `toolhive__linux_amd64.tar.gz` for Linux (x86_64) + - `toolhive__linux_arm64.tar.gz` for Linux (ARM64) + - `toolhive__windows_amd64.zip` for Windows (x86_64) + - `toolhive__windows_arm64.zip` for Linux (ARM64) + +3. Extract the archive and copy the binary to a directory in your PATH: + + macOS/Linux: + + ```bash + tar -xzf toolhive__.tar.gz + sudo install -m 0755 thv /usr/local/bin/ + ``` + + On Windows, extract the ZIP file to a folder and add that folder to your PATH + environment variable. + + + + +#### Prerequisites for building from source + +- Go 1.24 or newer +- Git +- Your `$GOPATH/bin` directory should be in your PATH + +#### Using Task (recommended) + +:::note + +The Task scripts currently only support macOS and Linux. Windows users should +use the pre-compiled binaries or build from source using Go tools. + +::: + +If you have [Task](https://taskfile.dev/installation/) installed: + +1. Clone the repository: + + ```bash + git clone https://github.com/stacklok/toolhive.git + cd toolhive + ``` + +2. Build and install the binary in your `$GOPATH/bin`: + + ```bash + task install + ``` + +#### Using Go tools + +1. Clone the repository: + + ```bash + git clone https://github.com/stacklok/toolhive.git + cd toolhive + ``` + +2. Build and install the binary in your `$GOPATH/bin`: + + ```bash + go install ./cmd/thv + ``` + + + + +## Verify your installation + +To verify that ToolHive is installed correctly: + +```bash +thv version +``` + +You should see output showing the version number, build date, and Git commit: + +```text title="ToolHive version output" +ToolHive v0.1.1 +Commit: 18956ca1710e11c9952d13a8dde039d5d1d147d6 +Built: 2025-06-30 13:59:34 UTC +Go version: go1.24.1 +Platform: darwin/arm64 +``` + +:::info[Privacy: Anonymous usage metrics] + +ToolHive collects anonymous usage metrics (tool call counts only) to help +improve the product. No personal information or sensitive data is collected. You +can disable this at any time using `thv config usage-metrics disable`. For more +information, see the [FAQ](../faq.mdx#does-toolhive-collect-any-data). + +::: + +## CLI conflict resolution + +If you have the ToolHive UI installed, it automatically manages the CLI for +version compatibility. ToolHive UI creates a symlink to its bundled CLI and +configures your shell's PATH, so you don't need to install the CLI separately. + +Running a standalone CLI binary (installed via Homebrew, WinGet, or manually) +while ToolHive UI is installed shows a conflict error: + +```text title="CLI conflict error" +Error: CLI conflict detected + +The ToolHive Desktop application manages a CLI installation at: + /Applications/ToolHive Studio.app/Contents/Resources/bin/darwin-arm64/thv + +You are running a different CLI binary at: + /usr/local/bin/thv + +To avoid conflicts, please use the desktop-managed CLI or uninstall +the ToolHive Desktop application. + +To use the desktop-managed CLI, ensure your PATH includes: + ~/.toolhive/bin + +Or run the desktop CLI directly: + ~/.toolhive/bin/thv [command] + +Desktop version: 0.8.3 +``` + +### Resolving the conflict + +If you see this error, you have two options: + +1. **Use the UI-managed CLI (recommended)**: Open a new terminal window to pick + up the PATH changes. The `thv` command should now use the UI-managed CLI. + +2. **Uninstall the standalone CLI**: If you want to use only the UI-managed CLI, + uninstall the standalone version: + - Homebrew: `brew uninstall thv` + - WinGet: `winget uninstall stacklok.thv` + - Manual: Remove the binary from your PATH + +:::note[Debugging override] + +For debugging purposes, you can bypass the conflict check by setting +`TOOLHIVE_SKIP_DESKTOP_CHECK=1`. This is not recommended for normal use as it +may cause version compatibility issues. + +::: + +## Upgrade ToolHive + +ToolHive automatically checks for updates and notifies you when a new version is +available. When you run a ToolHive command, it displays a message if an update +exists. + +To upgrade ToolHive: + + + + +If you installed ToolHive via Homebrew, upgrade it with: + +```bash +brew upgrade thv +``` + + + + +:::note + +On Windows, you must stop all running MCP servers before upgrading ToolHive, +otherwise the upgrade will fail because Windows locks the executable while it +runs. + +Run `thv stop --all` to stop all running servers. After you complete the +upgrade, restart them using `thv restart `. + +::: + +If you installed ToolHive via WinGet, upgrade it with: + +```bash +winget upgrade stacklok.thv +``` + + + + +Follow the same steps as the [initial installation](#install-toolhive), +downloading the latest release and overwriting the previous binary. + + + + +If you built ToolHive from source, upgrade it by pulling the latest changes and +rebuilding: + +#### Using Task: + +```bash +git pull + +task install +``` + +#### Using Go tools + +```bash +git pull + +go install ./cmd/thv +``` + + + + +## Get help with ToolHive commands + +ToolHive has built-in help for all commands: + +```bash +# General help +thv --help + +# Help for a specific command +thv --help +``` + +For detailed documentation on each command, see the +[CLI reference documentation](../reference/cli/thv.md). + +## Enable shell completion + +ToolHive can generate auto-completion scripts for your shell to make it easier +to use. The `thv completion` command generates scripts for bash, zsh, fish, and +PowerShell. + +Each shell has different requirements for where to store completion scripts and +how to enable them. The help output for each shell provides specific +instructions: + +```bash +# Get help on completion options +thv completion --help + +# Get specific instructions for your shell +thv completion bash --help +thv completion zsh --help +thv completion fish --help +thv completion powershell --help +``` + +## Uninstall ToolHive + +To uninstall ToolHive: + +1. First, remove any MCP servers managed by ToolHive: + + ```bash + # List running servers + thv list + # Stop and remove each server + thv stop --all + thv rm + ``` + +2. Remove all ToolHive configuration and log files: + + ```bash + # Remove the secrets encryption password entry from your OS keyring + thv secret reset-keyring + + # Delete the ToolHive configuration and log files + # macOS: + rm -rf ~/Library/Application\ Support/toolhive/ + + # Linux: + rm -rf ~/.config/toolhive/ + rm -rf ~/.local/share/toolhive/ + rm -rf ~/.local/state/toolhive/ + + # Windows: + Remove-Item "$env:LOCALAPPDATA\toolhive" -Recurse -Force + ``` + +3. Remove the ToolHive CLI: + +{/* prettier-ignore */} + + + + If you installed ToolHive via Homebrew, uninstall it with: + + ```bash + brew uninstall thv + ``` + + + + + If you installed ToolHive via WinGet, uninstall it with: + + ```bash + winget uninstall stacklok.thv + ``` + + + + + Delete the binary from your PATH: + + ```bash + sudo rm /usr/local/bin/thv + ``` + + + + + Remove the binary from your `$GOPATH`: + + ```bash + rm $(go env GOPATH)/bin/thv + ``` + + + + + +## Next steps + +Now that you have ToolHive installed, you can start using it to run and manage +MCP servers. See [Explore the registry](./registry.mdx) and +[Run MCP servers](./run-mcp-servers.mdx) to get started. + +## Related information + +- Quickstart: [Getting started with the ToolHive CLI](./quickstart.mdx) +- [`thv` CLI reference](../reference/cli/thv.md) +- [Client configuration](./client-configuration.mdx) +- [Secrets management](./secrets-management.mdx) + +## Troubleshooting + +
+Permission denied errors + +If you see "permission denied" errors when running ToolHive: + +1. Make sure the binary is executable: + + ```bash + chmod +x /path/to/thv + ``` + +2. If using Docker on Linux, make sure your user has permission to access the + Docker socket: + + ```bash + sudo usermod -aG docker $USER + ``` + + (Log out and back in or run `newgrp docker` for this to take effect) + + See + [Docker documentation](https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user) + for more details. + +
+ +
+Upgrade error on Windows + +If you encounter an error when upgrading ToolHive on Windows, it may be due to +the ToolHive executable being locked by a running MCP server proxy. + +```text title="Error example" +An unexpected error occurred while executing the command: +remove: Access is denied.: "C:\Users\USERNAME\AppData\Local\Microsoft\WinGet\Packages\stacklok.thv_Microsoft.Winget.Source_8wekyb3d8bbwe\thv.exe" +Uninstall failed with exit code: 0x8a150003 : Executing command failed +``` + +To resolve this: + +1. Stop all running MCP servers: + + ```powershell + thv stop --all + ``` + +2. After stopping all servers, run the upgrade command again: + + ```powershell + winget upgrade stacklok.thv + ``` + +3. If you still encounter issues, check if any ToolHive processes are still + running in the background. You can use Task Manager to end any lingering + `thv.exe` processes. + +4. Restart your MCP servers: + + ```powershell + thv list --all + thv restart + # repeat for each server + ``` + +
+ +### Other issues + +For other installation issues, check the +[GitHub issues page](https://github.com/stacklok/toolhive/issues) or join the +[Discord community](https://discord.gg/stacklok). diff --git a/versioned_docs/version-1.0/toolhive/guides-cli/manage-mcp-servers.mdx b/versioned_docs/version-1.0/toolhive/guides-cli/manage-mcp-servers.mdx new file mode 100644 index 00000000..6b98a568 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-cli/manage-mcp-servers.mdx @@ -0,0 +1,160 @@ +--- +title: Manage servers +description: How to monitor and manage the lifecycle of MCP servers using ToolHive. +--- + +## Monitoring + +ToolHive provides visibility into the status of your MCP servers. You can check +the status of running servers and view their logs using the ToolHive CLI. + +### List running servers + +To see all currently running MCP servers: + +```bash +thv list +``` + +This shows details about each running server, including its name, package +(container image), status, URL for connecting clients, group, and when it was +created. + +To include stopped servers in the list: + +```bash +thv list --all +``` + +### View server logs + +To view logs for an MCP server, use the +[`thv logs`](../reference/cli/thv_logs.md) command. You can optionally follow +the logs with the `--follow` option, which shows the most recent log entries and +updates in real time. + +#### Container logs + +For local servers, view the container logs: + +```bash +thv logs [--follow] +``` + +#### Proxy logs + +All servers (both local and remote) run through a proxy process managed by +ToolHive. To view the proxy logs instead of the container logs, use the +`--proxy` flag: + +```bash +thv logs --proxy [--follow] +``` + +For remote servers, the `--proxy` flag is required since there is no container +to read logs from. + +Alternatively, you can check the log files directly in the ToolHive application +directory. The path depends on your platform: + +- **macOS**: `~/Library/Application Support/toolhive/logs/.log` +- **Linux**: `~/.local/share/toolhive/logs/.log` +- **Windows**: `%LOCALAPPDATA%\toolhive\logs\.log` + +The specific log file path is displayed when you start a server with +[`thv run`](../reference/cli/thv_run.md). + +## Lifecycle management + +MCP servers can be started, stopped, restarted, and removed using the ToolHive +CLI. The commands are similar to Docker commands, but they're designed to work +with the ToolHive CLI and MCP servers. + +### Stop a server + +To stop a running MCP server: + +```bash +thv stop +``` + +This stops the server and the associated proxy process, removes the MCP server's +entry from your configured clients, but keeps the container for future use. For +remote servers, this terminates the proxy process but preserves the +configuration. + +Add the `--group` flag to stop all servers in a specific group: + +```bash +thv stop --group +``` + +Add the `--all` flag to stop all running servers. + +### Restart a server + +To start a stopped MCP server and add it back to your configured clients: + +```bash +thv start +``` + +For remote servers, restarting will: + +1. Recreate the proxy process +2. Re-establish connection to the remote server +3. Re-authenticate with the remote server (triggers new OAuth flow) + +Add the `--group` flag to start all servers in a specific group: + +```bash +thv start --group +``` + +### Remove a server + +To remove an MCP server: + +```bash +thv rm +``` + +This removes the container and cleans up the MCP server's entry in your +configured clients. If the server is still running, it will be stopped as part +of the removal. For remote servers, this removes the proxy process, +configuration, and stored authentication tokens. + +Add the `--group` flag to remove all servers in a specific group: + +```bash +thv rm --group +``` + +:::note + +If you use `docker rm` to remove an MCP container that ToolHive created, it +won't clean up the MCP server's entry in your configured clients. Use +[`thv rm`](../reference/cli/thv_rm.md) to make sure the entry is removed. + +::: + +### Remote server authentication + +Remote servers with OAuth authentication will automatically refresh tokens when +they expire during normal operation. However, starting a remote server always +triggers a new OAuth authentication flow: + +```bash +thv start +``` + +This will always prompt for re-authentication, even if valid tokens exist. + +## Related information + +- [`thv list` command reference](../reference/cli/thv_list.md) +- [`thv logs` command reference](../reference/cli/thv_logs.md) +- [`thv stop` command reference](../reference/cli/thv_stop.md) +- [`thv start` command reference](../reference/cli/thv_start.md) +- [`thv rm` command reference](../reference/cli/thv_rm.md) +- [Monitor with OpenTelemetry](../guides-cli/telemetry-and-metrics.mdx) diff --git a/versioned_docs/version-1.0/toolhive/guides-cli/network-isolation.mdx b/versioned_docs/version-1.0/toolhive/guides-cli/network-isolation.mdx new file mode 100644 index 00000000..2375ba07 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-cli/network-isolation.mdx @@ -0,0 +1,355 @@ +--- +title: Network isolation +description: How to configure network isolation for MCP servers in ToolHive +--- + +Most MCP servers require network access to function properly—for example, to +access APIs, download data, or communicate with other services. However, +malicious or misconfigured servers can also exfiltrate sensitive data or +download unwanted content. + +When you run an MCP server in ToolHive, you can optionally enable _network +isolation_. This feature restricts the MCP server's network access to only the +resources you specify. + +## Enable network isolation + +To enforce network access rules, use the `--isolate-network` flag when running +the MCP server. Network rules can come from: + +- The MCP server's default registry permissions +- A custom permission profile you create + +```bash +thv run --isolate-network [--permission-profile ] +``` + +:::tip + +You can combine file system and network permissions in the same permission +profile. For details on creating profiles that include both types of +permissions, see [custom permissions](./custom-permissions.mdx). + +::: + +When you enable network isolation, ToolHive creates a secure network +architecture around your MCP server that includes several components working +together to control network access. + +### Network architecture components + +Along with the main MCP server container, ToolHive launches additional +containers to manage network traffic: + +- An egress proxy container that filters outgoing network traffic +- A DNS container that provides controlled domain name resolution +- An ingress proxy container that handles incoming requests (only for MCP + servers using SSE or Streamable HTTP transport; stdio MCP servers don't need + this since they don't expose ports) + +### Network topology + +ToolHive creates two separate networks in the container runtime: + +- A shared external network (`toolhive-external`) that connects to your host's + network +- An internal network (`toolhive--internal`) for each MCP server + that isolates it from external access + +The MCP server container connects only to the internal network, while the proxy +and DNS containers connect to both networks. This design ensures that all +network traffic flows through controlled points, allowing ToolHive to enforce +the access rules you specify in your permission profile. + +The following diagrams show how network traffic flows through the isolation +architecture for different transport types: + + + + +For MCP servers using stdio transport, the ToolHive proxy process communicates +directly with the MCP server container through standard input and output. All +outbound network requests from the MCP server flow through the egress proxy and +DNS containers: + +```mermaid +architecture-beta + service mcp_client(server)[MCP client] + service toolhive_proxy(server)[ToolHive HTTP proxy process] + + group container_runtime[Container runtime] + group thv_internal[Isolated network] in container_runtime + service egress(server)[Egress proxy container] in container_runtime + service mcp_server(server)[MCP server container] in thv_internal + service dns_container(server)[DNS container] in container_runtime + + group external[External Resources] + service external_service(internet)[External service] in external + service external_dns(internet)[External DNS] in external + + junction outbound in container_runtime + + mcp_client:R --> L:toolhive_proxy + toolhive_proxy:R --> L:mcp_server + mcp_server:R -- L:outbound + egress:B <-- T:outbound + dns_container:T <-- B:outbound + egress:R --> L:external_service + dns_container:R --> L:external_dns +``` + + + + +For MCP servers using SSE or Streamable HTTP transport, ToolHive includes an +additional ingress proxy container. This proxy handles incoming HTTP requests +and ensures the MCP server remains isolated from direct external access: + +```mermaid +architecture-beta + service mcp_client(server)[MCP client] + service toolhive_proxy(server)[ToolHive HTTP proxy process] + + group container_runtime[Container runtime] + group thv_internal[Isolated network] in container_runtime + service ingress(server)[Ingress proxy container] in container_runtime + service egress(server)[Egress proxy container] in container_runtime + service mcp_server(server)[MCP server container] in thv_internal + service dns_container(server)[DNS container] in container_runtime + + group external[External resources] + service external_service(internet)[External service] in external + service external_dns(internet)[External DNS] in external + + junction outbound in container_runtime + + mcp_client:R --> L:toolhive_proxy + toolhive_proxy:R --> L:ingress + ingress:R --> L:mcp_server + mcp_server:R -- L:outbound + egress:B <-- T:outbound + dns_container:T <-- B:outbound + egress:R --> L:external_service + dns_container:R --> L:external_dns +``` + + + + +:::info[Important] + +Network isolation supports HTTP and HTTPS protocols. If your MCP server needs to +use other protocols (like direct TCP connections for database access), run it +without the `--isolate-network` flag and rely on the container's built-in +isolation instead. + +::: + +## Example: Enable network isolation using registry defaults + +Many MCP servers in the ToolHive registry have default permission profiles that +allow access to the specific resources they need. For example, the `atlassian` +MCP server has a default profile that allows access to Atlassian services. + +First, check the registry to see the default permissions for the `atlassian` MCP +server: + +```bash +thv registry info atlassian +``` + +Look for the `Permissions` section in the output: + +```text +Permissions: + Network: + Allow Host: .atlassian.net, .atlassian.com + Allow Port: 443 +``` + +To run the `atlassian` MCP server with these default permissions and enable +network isolation, use the following command: + +```bash +thv run --isolate-network atlassian +``` + +## Example: Customize network access + +The GitHub MCP server in the registry has a default profile that allows access +to `github.com`, but you might need to customize it for a self-hosted GitHub +Enterprise instance: + +```json title="github-enterprise-profile.json" +{ + "network": { + "outbound": { + "insecure_allow_all": false, + "allow_host": ["github.example.com"], + "allow_port": [443] + } + } +} +``` + +Run the GitHub MCP server with this profile: + +```bash +thv run --isolate-network --permission-profile ./github-enterprise-profile.json --secret github,target=GITHUB_PERSONAL_ACCESS_TOKEN github +``` + +## Example: Combined network and file system permissions + +Some MCP servers need both network restrictions and file system access. For +example, the `aws-diagram` MCP server generates diagrams locally but doesn't +need network access: + +```json title="aws-diagram-profile.json" +{ + "write": ["/tmp/generated-diagrams"], + "network": { + "outbound": { + "insecure_allow_all": false, + "allow_host": [], + "allow_port": [] + } + } +} +``` + +This profile: + +- Blocks all network access (equivalent to the `none` built-in profile) +- Allows the server to write generated diagrams to `/tmp/generated-diagrams` + +Run the server with this combined profile: + +```bash +thv run --isolate-network --permission-profile ./aws-diagram-profile.json aws-diagram +``` + +### Alternative: Using built-in profiles with volume mounts + +You can achieve the same result without creating a custom profile file by using +the built-in `none` profile and the `--volume` flag: + +```bash +thv run --isolate-network --permission-profile none --volume /home/user/aws-diagrams:/tmp/generated-diagrams aws-diagram +``` + +This approach is more flexible since you can easily change the host directory +without editing a profile file. + +## Accessing other workloads on the same container network + +ToolHive allows you to configure both outbound and inbound network access for +MCP servers. This is commonly needed when your MCP server needs to communicate +with databases, APIs, or other services that are running on your local host +during development, or when other containers need to communicate with your MCP +server. + +### Outbound access: MCP server to other workloads + +To allow an MCP server to access other workloads on the same network, you need +to configure outbound network isolation to include the appropriate hostnames and +ports. + +For example, in Docker environments, you can add `host.docker.internal` to +access services on the host. `host.docker.internal` is a special hostname +provided by Docker that resolves to the host machine's IP address from within +containers. + +Create a permission profile that allows this hostname and the required port: + +```json title="internal-access-profile.json" +{ + "network": { + "outbound": { + "insecure_allow_all": false, + "allow_host": ["host.docker.internal"], + "allow_port": [3000] + } + } +} +``` + +Run the MCP server with this profile: + +```bash +thv run --isolate-network --permission-profile ./internal-access-profile.json +``` + +### Inbound access: Other containers to MCP server + +By default, the ingress proxy only allows traffic from the container's own +hostname, `localhost`, and `127.0.0.1`. If you need to allow other containers or +workloads to communicate with your MCP server, configure the +`network.inbound.allow_host` setting in your permission profile. + +This is useful when: + +- Other containers need to call your MCP server's API +- You're running multiple services that need to communicate with each other +- You need to allow traffic from specific internal hostnames or domains + +Create a permission profile that allows specific inbound hostnames: + +```json title="inbound-access-profile.json" +{ + "network": { + "inbound": { + "allow_host": ["host.docker.internal", "localhost"] + } + } +} +``` + +Run the MCP server with this profile: + +```bash +thv run --isolate-network --permission-profile ./inbound-access-profile.json +``` + +:::info + +If no `network.inbound` configuration is specified, the ingress proxy uses the +default behavior of allowing traffic only from the container's own hostname, +`localhost`, and `127.0.0.1`. + +::: + +## Related information + +- [`thv run` command reference](../reference/cli/thv_run.md) +- [Run MCP servers](./run-mcp-servers.mdx) +- [Custom permissions](./custom-permissions.mdx) +- [File system access](./filesystem-access.mdx) + +## Troubleshooting + +
+Network connectivity issues + +If your MCP server can't connect to external services: + +1. Verify that your profile allows the necessary hosts and ports +2. Check that the transport protocol (TCP/UDP) is allowed +3. Check the logs of the egress proxy container for blocked requests: + + ```bash + docker logs -egress + ``` + + Look for messages indicating denied connections. + +4. Try temporarily using the default `network` profile to confirm it's a + permissions issue: + + ```bash + thv run --isolate-network --permission-profile network + ``` + +5. If the default profile works, review the egress proxy logs to identify which + specific permissions are missing in your custom profile. + +
diff --git a/versioned_docs/version-1.0/toolhive/guides-cli/quickstart.mdx b/versioned_docs/version-1.0/toolhive/guides-cli/quickstart.mdx new file mode 100644 index 00000000..35725d5e --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-cli/quickstart.mdx @@ -0,0 +1,335 @@ +--- +title: 'Quickstart: ToolHive CLI' +sidebar_label: Quickstart +description: + A step-by-step guide to installing the ToolHive CLI and running your first MCP + server. +--- + +In this tutorial, you'll install the ToolHive command-line application and run +your first MCP server. By the end, you'll have a working MCP server that can +fetch content from websites and be used by AI applications like GitHub Copilot +or Cursor. + +## What you'll learn + +- How to install ToolHive on your system +- How to find available MCP servers +- How to run an MCP server +- How to verify the server is working +- How to use the server with an AI client application + +## Prerequisites + +Before starting this tutorial, make sure you have: + +- [Docker](https://docs.docker.com/get-docker/) or + [Podman](https://podman-desktop.io/downloads) or + [Colima](https://github.com/abiosoft/colima) installed and running +- A [supported MCP client](../reference/client-compatibility.mdx) like GitHub + Copilot in VS Code, Cursor, Claude Code, and more + +## Step 1: Install ToolHive + +First, install ToolHive on your system. ToolHive provides the `thv` command-line +tool that manages MCP servers in secure containers. + +For detailed installation instructions, see the +[installing ToolHive](../guides-cli/install.mdx) guide. Here's a quick summary: + + + + +```bash +brew tap stacklok/tap +brew install thv +``` + + + + +```bash +winget install stacklok.thv +``` + +Open a new terminal window after installation to ensure the `thv` command is +available. + + + + +Download the appropriate binary for your platform from the +[ToolHive releases page](https://github.com/stacklok/toolhive/releases/latest). + +```bash +# Extract the archive +tar -xzf toolhive__.tar.gz +# Move the binary to a directory in your PATH +sudo mv thv /usr/local/bin/ +# Make it executable (if needed) +sudo chmod +x /usr/local/bin/thv +``` + + + + +### Verify your installation + +After installing, verify that ToolHive is working correctly: + +```bash +thv version +``` + +You should see output similar to this: + +```text +ToolHive v0.1.1 +Commit: 18956ca1710e11c9952d13a8dde039d5d1d147d6 +Built: 2025-06-30 13:59:34 UTC +Go version: go1.24.1 +Platform: darwin/arm64 +``` + +This confirms ToolHive is installed and ready to use. + +## Step 2: Register your client + +Next, run the ToolHive client setup command to register your MCP client: + +```bash +thv client setup +``` + +Select one or more clients from the list using the spacebar to toggle selection. +Press Enter to confirm your selection. + +:::info[What's happening?] + +When you run the setup command, ToolHive automatically finds +[supported clients](../reference/client-compatibility.mdx) on your system. When +you register a client, ToolHive automatically configures it to use MCP servers +that you run. This means you don't have to manually configure the client to +connect to the MCP server. + +::: + +Confirm that your client is registered successfully: + +```bash +thv client status +``` + +You should see output similar to this: + +```text +┌────────────────┬───────────┬────────────┐ +│ CLIENT TYPE │ INSTALLED │ REGISTERED │ +├────────────────┼───────────┼────────────┤ +│ vscode │ ✅ Yes │ ❌ No │ +└────────────────┴───────────┴────────────┘ +``` + +## Step 3: Find an MCP server to run + +See what MCP servers are available in the registry: + +```bash +thv registry list +``` + +You'll see output similar to this: + +```text +NAME DESCRIPTION TIER STARS PULLS +atlassian Model Context Protocol (MCP) server for Atlassian product... Community 2194 7789 +fetch A Model Context Protocol server that provides web content... Community 56714 9078 +github The GitHub MCP Server provides seamless integration with ... Official 16578 5000 +notion Official Notion MCP server. Official 2358 1109 +... +``` + +This shows all the MCP servers available in the ToolHive registry. + +:::info[What's happening?] + +ToolHive maintains a curated registry of MCP servers that have been verified to +work correctly. The registry includes information about what each server does +and how to use it. + +::: + +For this tutorial, you'll use the "fetch" server, which is a simple MCP server +that lets AI agents get the contents of a website. To learn more about the +server before running it, run: + +```bash +thv registry info fetch +``` + +This shows you detailed information about the server, including what tools it +provides and any configuration options. + +## Step 4: Run the Fetch MCP server + +Now, run the Fetch server: + +```bash +thv run fetch +``` + +ToolHive will pull the container image and start the server. You'll see output +similar to this: + +```text +8:41AM INF MCP server ghcr.io/stackloklabs/gofetch/server:latest is verified successfully +8:41AM INF Image ghcr.io/stackloklabs/gofetch/server:latest has 'latest' tag, pulling to ensure we have the most recent version... +8:41AM INF Pulling image: ghcr.io/stackloklabs/gofetch/server:latest +Pulling from stackloklabs/gofetch/server: latest +Digest: sha256:b9cbe3a8367f39e584d3fdd96d9c5046643c5f4798c3372b0c9049ece202cdef +Status: Image is up to date for ghcr.io/stackloklabs/gofetch/server:latest +8:41AM INF Successfully pulled image: ghcr.io/stackloklabs/gofetch/server:latest +8:41AM INF Using target port: 15266 +8:41AM INF Logging to: ~/Library/Application Support/toolhive/logs/fetch.log +8:41AM INF MCP server is running in the background (PID: 16834) +8:41AM INF Use 'thv stop fetch' to stop the server +``` + +:::info[What's happening?] + +When you run an MCP server, ToolHive: + +- Verifies the MCP server image provenance (if attestation information is + available in the registry) +- Downloads the container image +- Sets up the container with the necessary security settings and starts it in + the background +- Sets up a reverse proxy that lets your AI client applications communicate with + the server + +::: + +## Step 5: Verify the server is running + +Check that the server is running: + +```bash +thv list +``` + +You should see output similar to this: + +```text +NAME PACKAGE STATUS URL PORT TOOL TYPE CREATED AT +fetch ghcr.io/stackloklabs/gofetch/server:latest running http://127.0.0.1:15266/mcp 15266 mcp 2025-07-10 08:41:56 -0400 EDT +``` + +This confirms that the fetch server is running and available on port 15266. + +:::info[What's happening?] + +ToolHive keeps track of all the MCP servers it's managing. The +[`list`](../reference/cli/thv_list.md) command shows you which servers are +running and how they're configured. + +::: + +## Step 6: Use the MCP server with your AI client + +Now that your MCP server is running, you can use it with your AI client +application. Open your supported client (VS Code, Cursor, etc.) and ask the AI +to fetch content from a website. Note that you might need to restart your client +for the changes to take effect. + +For example, try asking: "Can you fetch the content from https://toolhive.dev +and summarize it for me?" + +The AI should be able to use the Fetch MCP server to retrieve the content and +provide a summary. + +:::info[What's happening?] + +When you ask the AI to fetch content, it detects that it needs external data. It +discovers the fetch tool provided by your MCP server, calls the tool with the +URL you specified, receives the webpage content, and then processes that content +to create a summary. + +::: + +## Step 7: Stop the server when you're done + +When you're finished using the server, you can stop it: + +```bash +thv stop fetch +``` + +If you want to remove it completely: + +```bash +thv rm fetch +``` + +:::info[What's happening?] + +Stopping a server pauses it and terminates the associated proxy process but +keeps the container around so you can start it quickly later using +[`thv start`](../reference/cli/thv_start.md) or +[`thv run`](../reference/cli/thv_run.md). Removing a server completely deletes +the container, freeing up resources. + +::: + +## What's next? + +Congratulations! You've successfully installed ToolHive and run your first MCP +server. Here are some next steps to explore: + +- Try running other MCP servers from the registry with + [`thv registry list`](../reference/cli/thv_registry_list.md) and + [`thv run`](../reference/cli/thv_run.md) +- Learn about [secrets management](../guides-cli/secrets-management.mdx) for MCP + servers that require authentication +- Explore [custom permissions](../guides-cli/custom-permissions.mdx) for MCP + servers +- Learn how to + [share and reuse server configurations](../guides-cli/run-mcp-servers.mdx#share-and-reuse-server-configurations) + using the export and import functionality +- Set up [shell completion](../guides-cli/install.mdx#enable-shell-completion) + to make ToolHive commands easier to use +- Learn how to [upgrade ToolHive](../guides-cli/install.mdx#upgrade-toolhive) + when new versions are available + +## Troubleshooting + +
+Server fails to start + +If the server fails to start, check: + +- Is Docker, Podman, or Colima running? +- Do you have internet access to pull the container image? +- Is the port already in use by another application? + +Try running with a specific port: + +```bash +thv run --proxy-port 8081 fetch +``` + +
+ +
+Client can't use the server + +If your AI client application can't use the server: + +- Make sure your client is registered with ToolHive (see Step 2) +- Check that your client is supported +- Restart your client to pick up the new configuration +- Verify the server is running with [`thv list`](../reference/cli/thv_list.md) + +
+ +If you encounter any problems, please join our +[Discord community](https://discord.gg/stacklok) for help. diff --git a/versioned_docs/version-1.0/toolhive/guides-cli/registry.mdx b/versioned_docs/version-1.0/toolhive/guides-cli/registry.mdx new file mode 100644 index 00000000..84a76aad --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-cli/registry.mdx @@ -0,0 +1,397 @@ +--- +title: Explore the registry +description: How to use the built-in registry to find MCP servers. +--- + +ToolHive includes a built-in registry of MCP servers with verified +configurations that meet a +[minimum quality standard](../concepts/registry-criteria.mdx), allowing you to +discover and deploy high-quality tools effortlessly. Simply select one from the +list and run it securely with a single command. + +## Find MCP servers + +To list all MCP servers in the ToolHive registry, run: + +```bash +thv registry list +``` + +This command displays a list of servers with their name, description, tier +(official or community), and the number of stars and downloads to help you +identify the most popular and useful servers. + +Example output: + +```text +NAME DESCRIPTION TIER STARS PULLS +atlassian Model Context Protocol (MCP) server for Atlassian product... Community 2194 7789 +elasticsearch Connect to your Elasticsearch data directly from any MCP ... Official 305 5429 +everything This MCP server attempts to exercise all the features of ... Community 56714 10441 +fetch A Model Context Protocol server that provides web content... Community 56714 9078 +filesystem Node.js server implementing Model Context Protocol (MCP) ... Community 56714 14041 +firecrawl A powerful web scraping and content extraction MCP server... Official 3605 7630 +git A Model Context Protocol server for Git repository intera... Community 56714 7000 +github The GitHub MCP Server provides seamless integration with ... Official 16578 5000 +grafana A Model Context Protocol (MCP) server for Grafana that pr... Official 1014 4900 +k8s MKP is a Model Context Protocol (MCP) server for Kubernet... Community 32 8064 + +<... trimmed for brevity ...> +``` + +You can also search by keyword to find servers related to a specific topic or +capability: + +```bash +thv search +``` + +For example, to locate servers related to GitHub: + +```bash +thv search github +``` + +## View server details + +To view detailed information about a specific MCP server, run: + +```bash +thv registry info +``` + +For example: + +```bash +thv registry info github +``` + +ToolHive provides the server's description, available tools, configuration +options, and other metadata. + +By default, ToolHive displays the server's configuration in a human-readable +format. To view the configuration in JSON format, use the `--format` option: + +```bash +thv registry info --format json +``` + +### Example output + +```yaml {1,11,19,24} showLineNumbers +Name: github +Image: ghcr.io/github/github-mcp-server:latest +Description: The GitHub MCP Server provides seamless integration with GitHub APIs, enabling advanced automation and interaction capabilities for developers and tools +Tier: Official +Status: Active +Transport: stdio +Repository URL: https://github.com/github/github-mcp-server +Has Provenance: Yes +Popularity: 13894 stars, 5000 pulls +Last Updated: 2025-05-20T00:21:46Z +Tools: + - get_me + - get_issue + - create_issue + - add_issue_comment + - list_issues +<... trimmed for brevity ...> + +Environment Variables: + - GITHUB_PERSONAL_ACCESS_TOKEN (required): GitHub personal access token with appropriate permissions + - GH_HOST: GitHub Enterprise Server hostname (optional) +Tags: + api, create, fork, github, list, pull-request, push, repository, search, update, issues +Permissions: + Network: + Allow Transport: tcp + Allow Host: docs.github.com, github.com + Allow Port: 443 +Example Command: + thv run github +``` + +This information helps you understand the server's capabilities, requirements, +and security profile before running it. + +- **Server name** (line 1): The server name to use with the + [`thv run`](../reference/cli/thv_run.md) command +- **Metadata** (lines 2-10): Details about the server, including the image name, + description, status, transport method, repository URL, whether the server has + SLSA provenance available for verification, and popularity +- **Tools list** (line 11): The list of tools this MCP server provides +- **Configuration** (line 19): Required and optional environment variables + needed to run the server +- **Permissions** (line 24): The permission profile applied to the server, + including file system and network access (see + [Custom permissions](./custom-permissions.mdx)) + +## Use a custom registry + +By default, ToolHive uses a built-in registry of verified MCP servers. You can +configure ToolHive to use a custom registry instead. This is useful for +organizations that want to maintain their own private registry of MCP servers. + +ToolHive supports two types of custom registries: + +- **File-based registries**: JSON files that follow either the + [ToolHive registry schema](../reference/registry-schema-toolhive.mdx) or the + [upstream registry schema](../reference/registry-schema-upstream.mdx) +- **API-based registries**: REST API endpoints that implement the + [MCP Registry API specification](../guides-registry/index.mdx), which use the + [upstream registry schema](../reference/registry-schema-upstream.mdx) + +Once you configure a custom registry, ToolHive uses it for all commands that +interact with the registry, such as `thv registry list`, `thv registry info`, +and `thv run`. + +### Set a remote registry + +To configure ToolHive to use a remote registry, set the registry URL or API +endpoint: + +```bash +thv config set-registry +``` + +The CLI automatically detects the registry type: + +- URLs ending with `.json` are treated as static registry files +- Other URLs are probed to detect MCP Registry API endpoints, falling back to + static files if the probe fails +- Local paths are treated as local registry files + +Examples: + +```bash +# Static registry file +thv config set-registry https://example.com/registry.json + +# API endpoint +thv config set-registry https://registry.example.com +``` + +### Set a local registry file + +To configure ToolHive to use a local registry file, set the file path: + +```bash +thv config set-registry +``` + +For example: + +```bash +thv config set-registry /path/to/local-registry.json +``` + +### Check the current registry + +To see which registry is currently configured: + +```bash +thv config get-registry +``` + +This displays the configured registry URL, API endpoint, or file path. If no +custom registry is configured, this command indicates that the built-in registry +is being used. + +### Revert to the built-in registry + +To remove the custom registry configuration and revert to using the built-in +registry: + +```bash +thv config unset-registry +``` + +This restores the default behavior of using ToolHive's built-in registry. + +## Organize servers with registry groups + +Registry groups allow you to organize related MCP servers and run them together +as a cohesive unit. This is particularly useful for creating team-specific +toolkits, workflow-based server collections, or environment-specific +configurations. + +:::note + +Registry groups are different from [runtime groups](./group-management.mdx). +Registry groups organize server definitions within registry files, while runtime +groups organize running server instances for access control. + +::: + +### Group structure + +Groups are defined as a top-level array in your custom registry: + +```json +{ + "servers": { + // Individual servers + }, + "groups": [ + { + "name": "group-name", + "description": "Description of what this group provides", + "servers": { + "server-name": { + // Complete server definition + } + }, + "remote_servers": { + "remote-server-name": { + // Complete remote server definition + } + } + } + ] +} +``` + +### Key characteristics + +- **Optional**: Groups are entirely optional. Omit the `groups` section if you + only need individual servers +- **Independent server definitions**: Each group contains complete server + configurations, not references to top-level servers +- **Self-contained**: Groups can define servers with the same names as top-level + servers but with different configurations +- **Flexible membership**: The same server can appear in multiple groups with + different configurations + +### Example: Multi-environment groups + +Here's an example showing how groups can organize servers for different +purposes: + +```json title='registry-with-groups.json' +{ + "$schema": "https://raw.githubusercontent.com/stacklok/toolhive-core/main/registry/types/data/toolhive-legacy-registry.schema.json", + "version": "1.0.0", + "last_updated": "2025-08-15T10:00:00Z", + "servers": { + "production-fetch": { + "description": "Production web content fetching server", + "image": "ghcr.io/stackloklabs/gofetch/server:latest", + "status": "Active", + "tier": "Community", + "transport": "streamable-http", + "permissions": { + "network": { + "outbound": { + "allow_host": [".company.com"], + "allow_port": [443] + } + } + } + } + }, + "groups": [ + { + "name": "devops-toolkit", + "description": "Complete DevOps toolkit for development teams", + "servers": { + "github": { + "description": "GitHub integration for repository and issue management", + "image": "ghcr.io/github/github-mcp-server:v0.15.0", + "status": "Active", + "tier": "Official", + "transport": "stdio", + "env_vars": [ + { + "name": "GITHUB_PERSONAL_ACCESS_TOKEN", + "description": "GitHub personal access token", + "required": true, + "secret": true + } + ] + }, + "heroku": { + "description": "Heroku platform deployment and management", + "image": "ghcr.io/stacklok/dockyard/npx/heroku-mcp-server:1.0.7", + "status": "Active", + "tier": "Community", + "transport": "stdio", + "env_vars": [ + { + "name": "HEROKU_API_KEY", + "description": "Heroku API key", + "required": true, + "secret": true + } + ] + } + } + }, + { + "name": "testing-suite", + "description": "Servers needed for automated testing workflows", + "servers": { + "test-fetch": { + "description": "Restricted fetch server for testing", + "image": "ghcr.io/stackloklabs/gofetch/server:latest", + "status": "Active", + "tier": "Community", + "transport": "streamable-http", + "args": ["--timeout", "5s"], + "permissions": { + "network": { + "outbound": { + "allow_host": ["test.example.com"], + "allow_port": [443] + } + } + } + } + } + } + ] +} +``` + +This registry provides: + +- A production-ready `production-fetch` server at the top level +- A `devops-toolkit` group with GitHub and Heroku servers for complete DevOps + workflows +- A `testing-suite` group with a restricted `test-fetch` server + +Notice how the `devops-toolkit` group contains multiple servers that work +together—GitHub for repository management and Heroku for +deployment—demonstrating how groups can bundle related functionality. + +### Run registry groups + +You can run entire groups using the group command: + +```bash +# Run all servers in the devops-toolkit group (GitHub + Heroku) +thv group run devops-toolkit + +# Run all servers in the testing-suite group +thv group run testing-suite + +# You can also pass environment variables and secrets to specific servers in the group +thv group run devops-toolkit --secret github-token,target=github.GITHUB_PERSONAL_ACCESS_TOKEN --secret heroku-key,target=heroku.HEROKU_API_KEY +``` + +Groups provide a convenient way to start multiple related servers with a single +command. + +## Next steps + +See [Run MCP servers](./run-mcp-servers.mdx) to run an MCP server from the +registry. + +Learn how to [create a custom MCP registry](../tutorials/custom-registry.mdx). + +## Related information + +- [`thv registry` command reference](../reference/cli/thv_registry.md) +- [`thv search` command reference](../reference/cli/thv_search.md) +- [`thv config set-registry` command reference](../reference/cli/thv_config_set-registry.md) +- [`thv config get-registry` command reference](../reference/cli/thv_config_get-registry.md) +- [`thv config unset-registry` command reference](../reference/cli/thv_config_unset-registry.md) diff --git a/versioned_docs/version-1.0/toolhive/guides-cli/run-mcp-servers.mdx b/versioned_docs/version-1.0/toolhive/guides-cli/run-mcp-servers.mdx new file mode 100644 index 00000000..f7a1ada3 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-cli/run-mcp-servers.mdx @@ -0,0 +1,1029 @@ +--- +title: Run MCP servers +description: How to run MCP servers with the ToolHive CLI. +--- + +This guide explains how to run Model Context Protocol (MCP) servers using +ToolHive. It covers how to run servers from the ToolHive registry, customize +server settings, and run custom servers using Docker images or protocol schemes. + +## Run a server from the registry + +To run an MCP server from the [ToolHive registry](./registry.mdx), use the +[`thv run`](../reference/cli/thv_run.md) command with the name of the server you +want to run. The server name is the same as its name in the registry. + +```bash +thv run +``` + +The ToolHive registry contains both local containerized MCP servers and remote +MCP servers. ToolHive automatically handles the appropriate setup based on the +server type. + +### Local containerized servers + +For example, to run the `fetch` server, which is a local containerized MCP +server that fetches website contents: + +```bash +thv run fetch +``` + +### Remote MCP servers + +Remote MCP servers in the registry don't run as local containers but instead use +ToolHive's transparent HTTP proxy to forward requests to remote servers. For +example: + +```bash +thv run neon +thv run stripe +``` + +When you run a remote server from the registry, ToolHive uses the pre-configured +remote URL and authentication settings. + +:::note[Naming convention] + +Remote MCP servers use the `-remote` suffix **when they have a local +containerized counterpart** to distinguish between the two versions. For +example: + +- `notion-remote` indicates this is the remote version of a server that also has + a local `notion` version +- `neon` and `stripe` don't have local counterparts, so they don't use the + `-remote` suffix + +To run a remote notion mcp server, you should use the `notion-remote` name. + +```bash +thv run notion-remote +``` + +Use `thv search ` or `thv registry list` to discover available servers. + +::: + +### Run registry groups + +If you use a [custom registry](./registry.mdx#use-a-custom-registry) that +includes groups, you can run multiple related servers together as a unit. This +is useful when you need several servers for a specific workflow or project: + +```bash +thv group run +``` + +For example, to run all servers in a group called `dev-toolkit`: + +```bash +thv group run dev-toolkit +``` + +Running a group starts all servers defined within that group simultaneously, +saving you from running each server individually. See +[Registry groups](./registry.mdx#organize-servers-with-registry-groups) for more +information about organizing servers into groups in your custom registry. + +:::info[What's happening?] + +When you run an MCP server from the registry, ToolHive handles different server +types automatically: + +**For local containerized servers:** + +1. Pulls the image and launches a container using the configuration from the + registry. +2. Starts an HTTP proxy process on a random port to forward client requests to + the container. +3. Labels the container so it can be tracked by ToolHive: + ```yaml + toolhive: true + toolhive-name: + ``` + +**For remote MCP servers:** + +1. Uses the pre-configured remote URL from the registry. +2. Automatically detects if the remote server requires authentication. +3. Handles OAuth/OIDC authentication flows if needed. +4. Starts an HTTP proxy process on a random port to forward client requests to + the remote server. +5. Manages the server like any other ToolHive workload. No container is created + for remote MCP servers. + +::: + +See [Run a custom MCP server](#run-a-custom-mcp-server) to run a server that is +not in the registry, or [Run a remote MCP server](#run-a-remote-mcp-server) for +more details about remote server configuration. + +## Customize server settings + +You might need to customize the behavior of an MCP server, such as changing the +port, mounting a local directory, or passing secrets. ToolHive provides several +options to customize the server's configuration when you run it. + +For a complete list of options, run +[`thv run --help`](../reference/cli/thv_run.md) or see the +[`thv run` command reference](../reference/cli/thv_run.md). + +### Run a server with a custom name + +By default, the container name matches the MCP server's name in the registry or +is automatically generated from the image name when you run a custom server. To +give your server instance a custom name, use the `--name` option: + +```bash +thv run --name +``` + +For example: + +```bash +thv run --name my-fetch fetch +``` + +### Run a server with secrets + +Many MCP servers require secrets or other configuration variables to function +correctly. ToolHive lets you pass these secrets as environment variables when +starting the server. + +To pass a secret to an MCP server, use the `--secret` option: + +```bash +thv run --secret ,target= +``` + +The `target` parameter specifies the name of the environment variable in the MCP +server's container. This is useful for passing secrets like API tokens or other +sensitive information. + +For example: + +```bash +thv run --secret github,target=GITHUB_PERSONAL_ACCESS_TOKEN github +``` + +See [Secrets management](./secrets-management.mdx) to learn how to manage +secrets in ToolHive. + +### Run a server within a group + +To run an MCP server within a specific group, use the `--group` option. This +allows you to organize your servers and manage them collectively. + +```bash +thv run --group +``` + +:::note + +The group must exist before you can run a server in it. + +::: + +See [Organize servers into groups](./group-management.mdx) to learn more about +organizing servers and configuring client access. + +### Mount a local file or directory + +To enable file system access for an MCP server, you can either use the +`--volume` flag to mount specific paths or create a custom permission profile +that defines read and write permissions. + +See [File system access](./filesystem-access.mdx) for detailed examples. To +prevent sensitive files from being exposed when mounting a project, use +[.thvignore](./thvignore.mdx). + +### Restrict network access + +To restrict an MCP server's network access, use the `--isolate-network` flag. +This enforces network access rules from either the server's default registry +permissions or a custom permission profile you create. + +See [Network isolation](./network-isolation.mdx) for network architecture +details and examples. + +### Add command-line arguments + +Some MCP servers require additional arguments to run correctly. You can pass +these arguments after the server name in the +[`thv run`](../reference/cli/thv_run.md) command: + +```bash +thv run -- +``` + +For example: + +```bash +thv run my-mcp-server:latest -- --arg1 value1 --arg2 value2 +``` + +Check the MCP server's documentation for the required arguments. + +:::warning + +Some MCP servers in the ToolHive registry include default arguments that are +essential for proper operation. When you provide custom arguments using +`-- `, these replace the registry defaults entirely rather than adding to +them. + +Before adding custom arguments, check the server's registry entry: + +```bash +thv registry info --format json | jq '.args' +``` + +If default arguments are listed, include them along with your custom arguments +to ensure the server functions correctly. + +::: + +### Run a server on a specific port + +ToolHive creates a reverse proxy on a random port that forwards requests to the +container. This is the port that client applications connect to. To set a +specific proxy port instead, use the `--proxy-port` flag: + +```bash +thv run --proxy-port +``` + +### Run a server exposing only selected tools + +ToolHive can filter the tools returned to the client as result of a `tools/list` +command as well as block calls to tools that you don't want to expose. + +This can help reduce the amount of tools sent to the LLM while still using the +same MCP server, but it is not meant as a security feature. + +To filter the list of tools, use the `--tools` flag either once + +```bash +thv run --tools +``` + +Or multiple times + +```bash +thv run --tools --tools +``` + +For example: + +```bash +thv run --tools list_issues --tools get_issue github +``` + +If the server comes from the registry, ToolHive can validate the tool names +against the list advertised in the image reference. An error is returned in case +ToolHive cannot find one of the specified tools. + +### Override tool names and descriptions + +With ToolHive you can modify how tools exposed by an MCP server are exposed to +clients. In particular, tool names and descriptions can be changed. + +This is useful when you want to guide an agent toward calling a specific tool +for particular questions. + +One common use case is running multiple copies of the same MCP server with +different [network access levels](./network-isolation.mdx)—one for internal +resources and another for public internet access. By overriding the tool names +and descriptions, you can help your agent choose the right server for each task. + +For example, the `fetch` MCP server exposes a single `fetch` tool with a +description like: + +``` +"Fetches a URL from the internet and optionally extracts its contents as markdown." +``` + +To override this, create a configuration file with one entry under +`toolsOverride` for each tool you want to modify: + +```json +{ + "toolsOverride": { + "fetch": { + "name": "toolhive-docs-fetch", + "description": "Fetches a URL from https://docs.stacklok.com/toolhive website." + } + } +} +``` + +Then pass this file to [`thv run`](../reference/cli/thv_run.md): + +```sh +thv run --tools-override override.json fetch +``` + +The key in the override object is the _original tool name_, while the `name` +field contains the _new name_ that clients will see. + +:::info + +Take care when using `--tools` and `--tools-override` together in the same +command. + +Tool filtering and tool overrides work independently: filtering limits access to +a subset of tools, while overrides change how those tools appear to clients. + +When using both options, `--tools` must reference the _overridden names_ (the +new names you define) since those are what clients will see. + +::: + +## Run a custom MCP server + +To run an MCP server that isn't in the registry, you can use a +[Docker image](#run-a-server-from-a-docker-image) or a +[protocol scheme](#run-a-server-using-protocol-schemes) to dynamically build the +server. + +ToolHive supports the following transport methods: + +- **Standard I/O** (`stdio`), default:\ + ToolHive redirects SSE or Streamable HTTP traffic from the client to the + container's standard input and output. This acts as a secure proxy, ensuring + that the container doesn't have direct access to the network or the host + machine. + +- **HTTP with SSE (server-sent events)** (`sse`):\ + ToolHive creates a reverse proxy that forwards requests to the container using + the HTTP/SSE protocol. + +- **Streamable HTTP** (`streamable-http`):\ + ToolHive creates a reverse proxy that forwards requests to the container using + the Streamable HTTP protocol, which replaced SSE in the MCP specification as + of the `2025-03-26` revision. + +:::info + +As of ToolHive CLI version 0.6.0, the default proxy mode for `stdio` MCP servers +is `streamable-http`. + +For backward compatibility with the deprecated SSE transport, you can explicitly +set the transport to `sse` using the `--proxy-mode sse` flag when running the +server. + +::: + +### Run a server from a Docker image + +To run an MCP server from a Docker image, specify the image name and tag in the +[`thv run`](../reference/cli/thv_run.md) command. You can also specify a custom +name for the server instance, the transport method, and any additional arguments +required by the MCP server. + +```bash +thv run [--name ] [--transport ] -- +``` + +For example, to run an MCP server from a Docker image named +`my-mcp-server-image` that uses the Streamable HTTP transport method and takes +additional arguments: + +```bash +thv run --name my-mcp-server --transport streamable-http my-mcp-server-image:latest -- --arg1 value1 --arg2 value2 +``` + +Check your MCP server's documentation for the required arguments. + +:::info[What's happening?] + +When you run an MCP server from a Docker image, ToolHive: + +1. Pulls the image (`my-mcp-server-image:latest`) and launches a container with + the options and arguments you specified. +2. Launches an HTTP proxy on a random port (optionally, add + `--proxy-port ` to specify the port). +3. Labels the container so it can be tracked by ToolHive: + ```yaml + toolhive: true + toolhive-name: my-mcp-server + ``` +4. Sets up the specified `--transport` method (`stdio`, `sse`, or + `streamable-http`). + +::: + +See [`thv run --help`](../reference/cli/thv_run.md) for more options. + +### Run a server using protocol schemes + +ToolHive also supports running MCP servers directly from package managers. This +means you can launch MCP servers without building or publishing a Docker image, +and without installing language-specific build tools on your machine. + +Currently, three protocol schemes are supported: + +- `uvx://`: For Python-based MCP servers using the uv package manager +- `npx://`: For Node.js-based MCP servers using npm +- `go://`: For Go-based MCP servers + +```bash +thv run ://@ +``` + +You'll likely need to specify additional arguments like the transport method, +volumes, and environment variables. Check your MCP server's documentation and +see [`thv run --help`](../reference/cli/thv_run.md) for more options. + +:::info[What's happening?] + +When you use a protocol scheme, ToolHive: + +1. Detects the protocol scheme and extracts the package reference +2. Generates a Dockerfile based on the appropriate template +3. Builds a Docker image with the package installed +4. Runs the MCP server using the new image (see + [Run a server from a Docker image](#run-a-server-from-a-docker-image) for + details) + +::: + +To build the image without running it, see +[Build MCP containers](./build-containers.mdx). + +#### Examples + + + + +The `uvx://` protocol is used for Python-based MCP servers. The package name +must be a valid package in the [PyPI registry](https://pypi.org/). The +`@` suffix is _optional_ and defaults to the latest version if omitted. + +```bash +thv run --name aws-docs uvx://awslabs.aws-documentation-mcp-server@latest +``` + + + + +The `npx://` protocol is used for Node.js-based MCP servers. The package name +must be a valid package in the [npm registry](https://www.npmjs.com/). The +`@` suffix is _optional_ and defaults to the latest version if omitted. + +```bash +thv run --name pulumi npx://@pulumi/mcp-server@latest +``` + + + + +The `go://` protocol is used for Go-based MCP servers. The package name must be +a valid Go module repo URI referencing the `main` package. The `@` +suffix is **required**. + +```bash +thv run --name grafana go://github.com/grafana/mcp-grafana/cmd/mcp-grafana@latest +``` + +You can also run a local Go module by specifying the path to the module: + +```bash +# Run from a relative path +thv run go://./cmd/my-mcp-server + +# Run from the current directory +cd my-go-mcp-project +thv run go://. + +# Run from an absolute path +thv run go:///path/to/my-go-project +``` + + + + +### Configure network transport + +When you run custom MCP servers using the SSE (`--transport sse`) or Streamable +HTTP (`--transport streamable-http`) transport method, ToolHive automatically +selects a random port to expose from the container to the host and sets the +`MCP_PORT` and `FASTMCP_PORT` environment variables in the container. + +```mermaid +graph LR + A[MCP client] -->|HTTP request to
proxy port: 5432| B[ToolHive proxy
process
Port: 5432] + B -->|Forwards to
host port: 9876| C[MCP container
Host: 9876 → Container: 7654
ENV: MCP_PORT=7654] + C -->|Response| B + B -->|Response| A +``` + +This is equivalent to running a Docker container with +`docker run -p : ...` + +For MCP servers that use a specific port or don't recognize those environment +variables, specify the container port for ToolHive to expose using the +`--target-port` flag: + +```bash +thv run --transport streamable-http --target-port +``` + +ToolHive still maps the container port to a random port on the host to avoid +conflicts with commonly used ports. This is equivalent to running a Docker +container with `docker run -p : ...` + +```mermaid +graph LR + A[MCP client] -->|HTTP request to
proxy port: 5432| B[ToolHive proxy
process
Port: 5432] + B -->|Forwards to
host port: 9876| C["MCP container
(--target-port 3000)
Host: 9876 → Container: 3000
ENV: MCP_PORT=3000"] + C -->|Response| B + B -->|Response| A +``` + +Some MCP servers use command-line arguments to specify their transport and port. +For example, if your server expects the transport type as a positional argument +and requires the `--port` flag, you can pass it like this: + +```bash +thv run --transport streamable-http --target-port -- http --port +``` + +Check your MCP server's documentation for the required transport and port +configuration. + +### Add a custom CA certificate + +In corporate environments with TLS inspection or custom certificate authorities, +you may need to configure a CA certificate for ToolHive to use when building +containers from protocol schemes like `uvx://`, `npx://`, and `go://`. + +ToolHive provides both global configuration and per-command options for CA +certificates. + +#### Configure a global CA certificate + +To set a CA certificate that ToolHive will use for all container builds: + +```bash +thv config set-ca-cert /path/to/corporate-ca.crt +``` + +To view the currently configured CA certificate: + +```bash +thv config get-ca-cert +``` + +To remove the CA certificate configuration: + +```bash +thv config unset-ca-cert +``` + +#### Override CA certificate per command + +You can override the global CA certificate configuration for a specific run +using the `--ca-cert` flag: + +```bash +thv run --ca-cert /path/to/other-ca.crt uvx://some-package +``` + +This is useful when you need to use different CA certificates for different +servers or when testing with a specific certificate. + +#### Priority order + +ToolHive uses the following priority order for CA certificates: + +1. Command-line flag (`--ca-cert`) +2. Global configuration (`thv config set-ca-cert`) +3. No custom CA certificate (default behavior) + +For example: + +```bash +# Set a global CA certificate +thv config set-ca-cert /path/to/corporate-ca.crt + +# This uses the configured CA certificate +thv run uvx://some-package + +# This overrides the configured CA certificate +thv run --ca-cert /path/to/special-ca.crt uvx://other-package +``` + +## Run a remote MCP server + +You can run remote MCP servers directly by providing their URL. This allows you +to connect to MCP servers hosted elsewhere without needing to manage containers +locally. ToolHive creates a transparent proxy that handles authentication and +forwards requests to the remote server. + +### Basic remote server setup + +To run a remote MCP server, simply provide its URL: + +```bash +thv run [--name ] +``` + +For example: + +```bash +thv run https://api.example.com/mcp +``` + +If you don't specify a name with `--name`, ToolHive will automatically derive a +name from the URL by extracting the main domain name (e.g., `notion` from +`https://api.notion.com/mcp`). + +By default, remote servers use the `streamable-http` transport. If the server +uses Server-Sent Events (SSE), specify the transport flag: + +```bash +thv run https://api.example.com/sse --transport sse +``` + +:::info[What's happening?] + +When you run a remote MCP server, ToolHive: + +1. Automatically detects if the remote server requires authentication. +2. Handles OAuth/OIDC authentication flows if needed. +3. Starts an HTTP proxy process on a random port to forward client requests to + the remote server. +4. Manages the server like any other ToolHive workload. No container is created + for remote MCP servers. + +::: + +### Authentication setup + +Many remote MCP servers require authentication. ToolHive supports automatic +authentication detection and OAuth/OIDC flows. + +#### Auto-detect authentication + +ToolHive can automatically detect if a remote server requires authentication by +examining the server's response headers and status codes: + +```bash +thv run https://protected-api.com/mcp --name my-server +``` + +If authentication is required, ToolHive will prompt you to complete the OAuth +flow. When no client credentials are provided, ToolHive automatically registers +an OAuth client with the authorization server using RFC 7591 dynamic client +registration, eliminating the need to pre-configure client ID and secret. + +#### OIDC authentication + +For servers using OpenID Connect (OIDC), provide the issuer URL, client ID, and +client secret obtained from the application provider: + +```bash +thv run https://api.example.com/mcp \ + --name my-server \ + --remote-auth-issuer https://auth.example.com \ + --remote-auth-client-id my-client-id \ + --remote-auth-client-secret my-client-secret +``` + +#### OAuth2 authentication + +For servers using OAuth2, specify the authorization and token URLs along with +the client ID and secret obtained from the application provider: + +```bash +thv run https://api.example.com/mcp \ + --name my-server \ + --remote-auth-authorize-url https://auth.example.com/oauth/authorize \ + --remote-auth-token-url https://auth.example.com/oauth/token \ + --remote-auth-client-id my-client-id \ + --remote-auth-client-secret my-client-secret +``` + +#### Automatic token refresh + +ToolHive automatically handles OAuth token refresh for remote MCP servers. When +you authenticate with a remote server, ToolHive stores both the access token and +refresh token securely. The refresh token is used to automatically obtain new +access tokens when they expire, ensuring uninterrupted service without requiring +manual re-authentication. + +### Advanced remote server configuration + +#### OAuth scopes and parameters + +Specify custom OAuth scopes for the authentication flow: + +```bash +thv run https://api.example.com/mcp \ + ... \ + --remote-auth-scopes read,write,admin +``` + +#### Resource indicator (RFC 8707) + +When authenticating to remote MCP servers, you can specify a resource indicator +as defined by [RFC 8707](https://datatracker.ietf.org/doc/html/rfc8707). This +allows the authorization server to return an access token with a scoped +audience, which will then be passed to and validated by the remote MCP server. + +By default, ToolHive automatically uses the remote server URL as the resource +indicator when authenticating. The URL is validated, normalized (lowercase +scheme and host, fragments stripped), and included in the OAuth token request. + +To explicitly set a different resource indicator, use the +`--remote-auth-resource` flag: + +```bash +thv run https://api.example.com/mcp \ + ... \ + --remote-auth-resource https://api.example.com +``` + +The resource parameter must include a scheme and host, and cannot contain +fragments. If you provide an invalid resource parameter, ToolHive will return an +error. + +#### Custom authentication timeout + +Adjust the authentication timeout for slow networks: + +```bash +thv run https://api.example.com/mcp \ + ... \ + --remote-auth-timeout 2m +``` + +#### Skip browser authentication + +For headless environments, skip the browser-based OAuth flow: + +```bash +thv run https://api.example.com/mcp \ + ... \ + --remote-auth-skip-browser +``` + +#### Inject custom headers + +Some remote MCP servers require custom headers for tenant identification, API +keys, or other purposes. ToolHive can inject headers into every request +forwarded to the remote server. + +To add plaintext headers, use the `--remote-forward-headers` flag: + +```bash +thv run https://api.example.com/mcp \ + --name my-server \ + --remote-forward-headers "X-Tenant-ID=tenant123" \ + --remote-forward-headers "X-Custom-Header=value" +``` + +For sensitive values like API keys, use the `--remote-forward-headers-secret` +flag to reference values stored in ToolHive's secrets manager: + +```bash +# First, store the secret (enter the value when prompted) +thv secret set my-api-key + +# Then reference it by name +thv run https://api.example.com/mcp \ + --name my-server \ + --remote-forward-headers-secret "X-Api-Key=my-api-key" +``` + +You can combine plaintext and secret-backed headers in a single command: + +```bash +thv run https://api.example.com/mcp \ + --name my-server \ + --remote-forward-headers "X-Tenant-ID=tenant123" \ + --remote-forward-headers-secret "X-Api-Key=my-api-key" +``` + +:::warning[Security considerations] + +- Plaintext header values are stored in the server's configuration file. For + sensitive values (API keys, tokens), always use + `--remote-forward-headers-secret`. +- Secret-backed header values are resolved at runtime and never stored in + configuration files. +- Certain headers cannot be configured for security reasons, including `Host`, + `Connection`, `Transfer-Encoding`, and proxy-related headers like + `X-Forwarded-For`. + +::: + +### Remote server management + +Remote MCP servers are managed like any other ToolHive workload: + +- **List servers**: `thv list` shows remote servers with their target URLs +- **View logs**: `thv logs ` shows proxy and authentication logs +- **Stop/restart**: `thv stop ` and `thv restart ` +- **Remove**: `thv rm ` removes the proxy and configuration + +## Share and reuse server configurations + +ToolHive allows you to export a server's configuration and run servers using +previously exported configurations. This is useful for: + +- Sharing server setups with team members +- Creating backups of complex configurations +- Running identical server instances across different environments + +### Export a server configuration + +To export a running server's configuration to a file, use the +[`thv export`](../reference/cli/thv_export.md) command: + +```bash +thv export +``` + +For example, to export the configuration of a running server named "fetch": + +```bash +thv export fetch ./fetch-config.json +``` + +This creates a JSON file containing all the server's configuration, including: + +- Container image and version +- Environment variables and secrets references +- Volume mounts and permissions +- Network settings +- Transport configuration + +### Run a server from an exported configuration + +To run a server using a previously exported configuration, use the +[`thv run`](../reference/cli/thv_run.md) command with the `--from-config` flag: + +```bash +thv run --from-config +``` + +For example, to run a server using the exported configuration: + +```bash +thv run --from-config ./fetch-config.json +``` + +This creates a new server instance with identical settings to the original. If +the original server used secrets, you must have the same secrets available in +your ToolHive secrets store. + +:::note + +When you use `--from-config`, you cannot specify any other command-line flags. +The configuration file must contain all server settings. + +::: + +## Next steps + +See [Monitor and manage MCP servers](./manage-mcp-servers.mdx) to monitor and +control your servers. + +[Test your MCP server](./test-mcp-servers.mdx) using the MCP Inspector or +`thv mcp` commands. + +## Related information + +- [`thv run` command reference](../reference/cli/thv_run.md) +- [Client configuration](./client-configuration.mdx) +- [Secrets management](./secrets-management.mdx) +- [Custom permissions](./custom-permissions.mdx) + - [File system access](./filesystem-access.mdx) + - [Network isolation](./network-isolation.mdx) + +## Troubleshooting + +
+Server fails to start + +If a server fails to start: + +1. Check if Docker, Podman, or Colima is running +2. Verify you have internet access to pull images +3. Check if the port is already in use +4. Look at the error message for specific issues + +
+ +
+Server starts but isn't accessible + +If a server starts but isn't accessible: + +1. Check the server logs: + + ```bash + thv logs + ``` + +2. Verify the port isn't blocked by a firewall + +3. Make sure clients are properly configured (see + [Client configuration](./client-configuration.mdx)) + +
+ +
+Server crashes or exits unexpectedly + +If a server crashes or exits unexpectedly: + +1. List all MCP servers including stopped ones: + + ```bash + thv list --all + ``` + +2. Check the logs for error messages: + + ```bash + thv logs + ``` + +3. Check if the server requires any secrets or environment variables + +4. Verify the server's configuration and arguments + +
+ +
+Remote server authentication fails + +If a remote MCP server authentication fails: + +1. Check the server logs for authentication errors (see + [View server logs](./manage-mcp-servers.mdx#view-server-logs) for the correct + log file path on your platform) + +2. Verify the issuer URL is correct and accessible + +3. Check if the OAuth client ID and secret are valid + +4. Ensure the remote server supports the OAuth flow you're using + +5. Try re-authenticating by restarting the server: + + ```bash + thv restart + ``` + +
+ +
+Remote server connection issues + +If you can't connect to a remote MCP server: + +1. Verify the remote server URL is correct and accessible + +2. Check your network connectivity: + + ```bash + curl -I https://api.example.com/mcp + ``` + +3. Check if the remote server requires specific headers or authentication + +4. Review the server logs for connection errors: + + ```bash + thv logs + ``` + +5. Try running with debug mode for more detailed logs: + + ```bash + thv run --debug https://api.example.com/mcp --name my-server + ``` + +
+ +
+MCP server can't connect to services on the same host + +When ToolHive runs MCP servers in containers, they can't reach services on your +host machine using `localhost` due to container network isolation. + +Replace `localhost` in your MCP server configuration with the appropriate host +address for your platform: + +- **Docker Desktop (macOS/Windows)**: `host.docker.internal` +- **Podman Desktop**: `host.containers.internal` +- **Docker Engine (Linux)**: `172.17.0.1` (or your custom bridge gateway IP) + +For example, change `http://localhost:3000` to +`http://host.docker.internal:3000`. + +
diff --git a/versioned_docs/version-1.0/toolhive/guides-cli/secrets-management.mdx b/versioned_docs/version-1.0/toolhive/guides-cli/secrets-management.mdx new file mode 100644 index 00000000..3bbbed6c --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-cli/secrets-management.mdx @@ -0,0 +1,362 @@ +--- +title: Secrets management +description: How to securely manage API tokens and other sensitive data in ToolHive. +--- + +MCP servers often need secrets like API tokens, connection strings, and other +sensitive parameters. ToolHive provides built-in secrets management features, +letting you manage these values securely without exposing them in plaintext +configuration files. + +## Secrets providers + +ToolHive supports multiple secrets providers to fit different security and +workflow requirements: + +- `encrypted` - ToolHive encrypts secrets using a password stored in your + operating system's keyring +- `1password` - ToolHive retrieves secrets from a 1Password vault + +You can use only one provider at a time. To select your preferred provider, run: + +```bash +thv secret setup +``` + +If you plan to use 1Password, first set up a 1Password service account and +obtain an API token. See the 1Password tab below for details. + + + + +When you select the `encrypted` provider, ToolHive prompts you to create an +encryption password that protects your secrets. + +ToolHive stores this encryption password in your operating system's keyring +(Keychain Access on macOS, Credential Manager on Windows, and dbus/Gnome Keyring +on Linux). This means you don't need to enter the password every time you use a +[`thv secret`](../reference/cli/thv_secret.md) command. + + + + +:::note + +The 1Password provider is read-only. You can list and view secrets, but you +can't create or delete them through ToolHive. Secrets must already exist in your +1Password vault. + +If you'd like to see write operations added, please +[open an issue](https://github.com/stacklok/toolhive/issues) or join the +`#toolhive-developers` channel in [Discord](https://discord.gg/stacklok). +Contributions are welcome! + +::: + +To use 1Password as your secrets provider, set up a 1Password service account. +For detailed instructions, see the +[1Password documentation](https://developer.1password.com/docs/service-accounts/get-started#create-a-service-account). + +Next, set the `OP_SERVICE_ACCOUNT_TOKEN` environment variable to your service +account's API token (displayed during the service account creation process). +This token is required for all [`thv secret`](../reference/cli/thv_secret.md) +commands: + +```bash +export OP_SERVICE_ACCOUNT_TOKEN= +``` + +Then, run `thv secret setup` and select `1password` when prompted. + +To reference a secret from 1Password, use the +[1Password secret reference](https://developer.1password.com/docs/cli/secret-reference-syntax) +URI format: + +```text +op:////[section-name/] +``` + +For example, to retrieve the `password` field from the `github` item in the +`MCPVault` vault: + +```bash +thv secret get op://MCPVault/github/password +``` + +Run [`thv secret list`](../reference/cli/thv_secret_list.md) to see all secrets +accessible to your service account, along with their URIs. + + + + +## Manage secrets + +### Create or update a secret + +The [`thv secret set`](../reference/cli/thv_secret_set.md) command lets you +create or update a secret in your secret store. You can set a secret +interactively by running: + +```bash +thv secret set +``` + +ToolHive prompts you to enter the secret value, and the input remains hidden for +security. + +Example: + +```bash +thv secret set github +# Enter your GitHub personal access token when prompted +``` + +Alternatively, you can set a secret using standard input: + +```bash +echo "MY_SECRET_VALUE" | thv secret set +``` + +:::tip[Example] + +Create a secret named `github` and set its value to your GitHub authentication +token using the GitHub CLI: + +```bash +gh auth token | thv secret set github +``` + +::: + +### List and view secrets + +To list the names of all secrets in your secret store without revealing their +values: + +```bash +thv secret list +``` + +To decrypt and view a secret's value: + +```bash +thv secret get +``` + +### Remove a secret + +To delete a secret when it's no longer needed: + +```bash +thv secret delete +``` + +### Reset your secret store + +ToolHive doesn't currently support changing the encryption password. If you need +to reset your secret store, delete the encrypted secrets file and recreate your +secrets. + +First, remove the encryption password from the keyring: + +```bash +thv secret reset-keyring +``` + +Then, delete the encrypted secrets file: + + + + + ```bash + rm ~/Library/Application\ Support/toolhive/secrets_encrypted + ``` + + + + + ```bash + rm ~/.config/toolhive/secrets_encrypted + ``` + + + + + ```powershell + Remove-Item "$env:LOCALAPPDATA\toolhive\secrets_encrypted" + ``` + + + + +The next time you run a `thv secret` command, ToolHive prompts you to create a +new encryption password and starts with a fresh secret store. + +## Use secrets with MCP servers + +ToolHive can securely pass secrets to an MCP server when you run it. This lets +the server access sensitive information without exposing it in plaintext. + +To do this, use the `--secret` flag with the +[`thv run`](../reference/cli/thv_run.md) command. The secret value is injected +into the container as an environment variable. + +```bash +thv run --secret ,target= +``` + +Check the MCP server's documentation to find the expected environment variable +names. For example, the GitHub MCP server expects the GitHub token to be passed +as `GITHUB_PERSONAL_ACCESS_TOKEN`. + +For MCP servers in the ToolHive registry, you can find the expected environment +variable names in the server's registry entry: + +```bash +thv registry info +``` + +### Example: GitHub API token + +This example shows how to set up a GitHub authentication token and use it with +the GitHub MCP server: + +1. Set the secret: + + ```bash + thv secret set github + # Enter your GitHub personal access token when prompted + ``` + +2. Run the GitHub MCP server with the token: + + ```bash + thv run --secret github,target=GITHUB_PERSONAL_ACCESS_TOKEN github + ``` + +The GitHub MCP server now has access to your GitHub token and can make +authenticated API requests. + +### Example: Multiple secrets + +You can provide multiple secrets to a server by using the `--secret` flag +multiple times: + +```bash +thv run \ + --secret github,target=GITHUB_TOKEN \ + --secret openai,target=OPENAI_API_KEY \ + multi-api-server +``` + +### Example: 1Password secret + +To use a secret from 1Password with an MCP server, set the +`OP_SERVICE_ACCOUNT_TOKEN` environment variable with your 1Password service +account API token and reference the secret using the `op://` URI format. + +```bash +OP_SERVICE_ACCOUNT_TOKEN= thv run \ + --secret op://MCPVault/slackbot/token,target=SLACK_BOT_TOKEN \ + --secret op://MCPVault/slackbot/team_id,target=SLACK_TEAM_ID \ + slack +``` + +This command retrieves the `token` and `team_id` fields from the `slackbot` item +in the `MCPVault` vault and passes them to the `slack` MCP server as the +`SLACK_BOT_TOKEN` and `SLACK_TEAM_ID` environment variables. + +## Related information + +- [`thv secret` command reference](../reference/cli/thv_secret.md) +- [Run MCP servers](../guides-cli/run-mcp-servers.mdx) + +## Troubleshooting + +
+Keyring access issues + +If you run into errors related to the system keyring: + +1. Make sure your system's keyring service is running +2. Check that you have the necessary permissions +3. On some Linux systems, you might need to install additional packages: + + ```bash + # For Debian/Ubuntu + sudo apt-get install gnome-keyring + + # For Fedora/RHEL + sudo dnf install gnome-keyring + ``` + +
+ +
+Secret not available to MCP server + +If your MCP server can't access a secret: + +1. Verify the secret exists: + + ```bash + thv secret list + ``` + +2. Verify the secret value: + + ```bash + thv secret get + ``` + +3. Check that you're using the correct secret name and target environment + variable. Inspect the MCP server's expected environment variables in the + registry: + + ```bash + thv registry info + ``` + +4. Inspect the server logs for any errors: + + ```bash + thv logs + ``` + +
+ +
+Forgot encryption password + +If the keyring entry is lost or corrupted and you forget your encryption +password, you won't be able to access your secrets. In this case, delete the +[encrypted secrets file](#reset-your-secret-store) and recreate your secrets. + +
+ +
+Issues accessing 1Password secrets + +If you can't access 1Password secrets: + +1. Verify the `OP_SERVICE_ACCOUNT_TOKEN` environment variable is set: + + ```bash + echo $OP_SERVICE_ACCOUNT_TOKEN + ``` + +2. Check that the token is valid and has the necessary permissions to access the + vault and item: + + ```bash + thv secret list + ``` + +3. Make sure the secret reference URI is correct and matches the vault, item, + and field names in 1Password: + + ```bash + thv secret get op:////[section-name/] + ``` + +
diff --git a/versioned_docs/version-1.0/toolhive/guides-cli/skills-management.mdx b/versioned_docs/version-1.0/toolhive/guides-cli/skills-management.mdx new file mode 100644 index 00000000..1f107ac9 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-cli/skills-management.mdx @@ -0,0 +1,434 @@ +--- +title: Manage agent skills +sidebar_label: Agent skills +description: How to install, distribute, and manage agent skills with the ToolHive CLI. +--- + +[Agent skills](https://agentskills.io/) are reusable bundles of instructions, +scripts, and resources that teach AI agents how to perform specific tasks. While +MCP servers provide **tools** (the raw capabilities an agent can call), skills +provide the **knowledge** of when, why, and how to use those tools effectively. + +ToolHive lets you install skills from a +[ToolHive Registry Server](../guides-registry/skills.mdx) (by name), OCI +registries (by reference), or Git repositories (by URL), and manages their +lifecycle across multiple AI clients. + +:::tip[New to skills?] + +If you're not sure what skills are or how they relate to MCP servers, see +[Understanding skills](../concepts/skills.mdx) for a conceptual overview. + +::: + +## Prerequisites + +- The [ToolHive API server](./api-server.mdx) must be running. Start it in a + separate terminal window (the command blocks while running): + + ```bash + thv serve + ``` + + The server must remain running while you use `thv skill` commands. + + :::tip[Using the ToolHive desktop app?] + + If the ToolHive desktop app is already running, the API server is available + automatically. You can skip the `thv serve` step and use `thv skill` commands + directly. + + ::: + +- A supported AI client installed. See the + [client compatibility reference](../reference/client-compatibility.mdx) for + which clients support skills. + +## Install a skill + +You can install skills from a ToolHive Registry Server (by name), an OCI +registry (by reference), or a Git repository (by URL). + +### Install from the registry + +The simplest way to install a skill is by name. ToolHive looks up the skill in +the configured Registry Server and installs it automatically: + +```bash +thv skill install +``` + +For example: + +```bash +thv skill install toolhive-cli-user +``` + +### Install from an OCI registry + +To install a specific version of a skill from an OCI registry, provide the full +OCI reference: + +```bash +thv skill install ghcr.io//skills/: +``` + +### Install from a Git repository + +To install a skill directly from a Git repository: + +```bash +thv skill install git://github.com/anthropics/skills@main#skills/webapp-testing +``` + +The URL format is `git://host/owner/repo[@ref][#path/to/skill]`, where `@ref` is +a branch, tag, or commit hash, and `#path` points to the skill subdirectory. + +:::info[What's happening?] + +When you install a skill, ToolHive: + +1. Resolves the skill source (registry, OCI, or Git) +2. Downloads and extracts the skill files +3. Writes the `SKILL.md` and supporting files to your AI client's skills + directory +4. Records the installation in its database + +Your AI client discovers the skill automatically by reading from its skills +directory. + +::: + +### Overwrite an existing skill + +If a skill is already installed and you want to replace it, use the `--force` +flag: + +```bash +thv skill install my-skill --force +``` + +### Target a specific client + +If you have multiple supported clients, ToolHive installs the skill for the +first one it detects. To control which client receives the skill, use the +`--client` flag: + +```bash +thv skill install my-skill --client claude-code +``` + +See the [client compatibility reference](../reference/client-compatibility.mdx) +for the full list of clients that support skills. + +### Add a skill to a group + +You can organize skills into groups, just like MCP servers. Skills in a group +are automatically installed to clients registered with that group: + +```bash +thv skill install my-skill --group development +``` + +## Choose a scope + +Skills support two scopes that control where the skill files are installed: + +- **User scope** (default): Installs the skill globally for your user account. + The skill is available across all projects. +- **Project scope**: Installs the skill in a specific project directory. The + skill is only available when working in that project. + +### Install a user-scoped skill + +```bash +thv skill install my-skill +``` + +This installs the skill to your home directory (for example, +`~/.claude/skills/my-skill/` for Claude Code). + +### Install a project-scoped skill + +```bash +thv skill install my-skill --scope project --project-root /path/to/project +``` + +This installs the skill to the project directory (for example, +`/path/to/project/.claude/skills/my-skill/` for Claude Code). + +:::note + +The project root must be a Git repository. ToolHive validates this to prevent +installing skills in arbitrary directories. + +::: + +## List installed skills + +To see all installed skills: + +```bash +thv skill list +``` + +The output shows the name, version, scope, status, clients, and source reference +for each skill. + +### Filter the list + +You can filter skills by scope, client, or group: + +```bash +thv skill list --scope user +thv skill list --client claude-code +thv skill list --group development +``` + +### Get JSON output + +```bash +thv skill list --format json +``` + +## View skill details + +To see detailed information about a specific skill: + +```bash +thv skill info +``` + +This shows the skill's name, version, description, scope, status, source +reference, installation date, and associated clients. + +## Uninstall a skill + +To remove an installed skill: + +```bash +thv skill uninstall +``` + +For project-scoped skills, specify the scope and project root: + +```bash +thv skill uninstall my-skill --scope project --project-root /path/to/project +``` + +This removes the skill files from all associated client directories and deletes +the database record. + +## Create a skill + +Every skill requires a `SKILL.md` file at the root of its directory. At a +minimum, the file needs `name` and `description` fields in YAML frontmatter, +followed by Markdown instructions for the agent: + +```yaml title="my-skill/SKILL.md" +--- +name: my-skill +description: >- + What this skill does and when to use it. Include keywords that help agents + identify relevant tasks. +--- +# Instructions + +Step-by-step instructions for the agent... +``` + +The `name` must be lowercase alphanumeric with hyphens, must match the directory +name, and must not start or end with a hyphen. + +You can also add optional files in `scripts/`, `references/`, and `assets/` +subdirectories for executable code, documentation, and static resources. + +For the full list of frontmatter fields (version, license, allowed-tools, and +more) and directory structure details, see +[Understanding skills](../concepts/skills.mdx#skill-structure). + +## Build and publish skills + +Once you've created a skill, you can package it as an OCI artifact and publish +it to a registry for others to install. + +### Validate a skill + +Before building, validate your skill directory to check for errors: + +```bash +thv skill validate ./my-skill +``` + +This verifies that: + +- A `SKILL.md` file exists with valid frontmatter +- The `name` and `description` fields are present +- The `name` matches the directory name +- No symlinks or path traversal issues exist + +### Build an OCI artifact + +Package your skill into an OCI artifact: + +```bash +thv skill build ./my-skill +``` + +To specify a custom tag: + +```bash +thv skill build ./my-skill --tag ghcr.io/my-org/skills/my-skill:v1.0.0 +``` + +The build command stores the artifact in your local OCI store and prints the +reference. + +### Push to a registry + +After building, push the artifact to a remote OCI registry: + +```bash +thv skill push ghcr.io/my-org/skills/my-skill:v1.0.0 +``` + +:::note + +Pushing to a remote registry uses your existing container registry credentials +(for example, from `docker login` or `podman login`). Make sure you're +authenticated before pushing. + +::: + +## List and remove locally-built skill artifacts + +After building skills locally, you can view and manage the artifacts stored in +your local OCI store. + +### List locally-built artifacts + +```bash +thv skill builds +``` + +This lists all OCI skill artifacts built locally with `thv skill build`. The +output shows the tag, digest, name, and version of each artifact: + +```text +TAG DIGEST NAME VERSION +ghcr.io/my-org/skills/my-skill:v1.0.0 sha256:a1b2c3d4... my-skill 1.0.0 +my-skill:latest sha256:e5f6a7b8... my-skill +``` + +For JSON output: + +```bash +thv skill builds --format json +``` + +### Remove a locally-built artifact + +To remove an artifact from the local OCI store: + +```bash +thv skill builds remove +``` + +For example: + +```bash +thv skill builds remove my-skill:latest +``` + +This removes the artifact and cleans up its blobs from the local store. If +multiple tags share the same digest, the blobs are retained until all tags +pointing to that digest are removed. + +## Next steps + +- [Configure your AI client](./client-configuration.mdx) to register clients + with ToolHive for automatic MCP server and skill configuration +- [Manage skills in the registry](../guides-registry/skills.mdx) to publish + skills for your team + +## Related information + +- [Understanding skills](../concepts/skills.mdx) for a conceptual overview +- [`thv skill` command reference](../reference/cli/thv_skill.md) +- [`thv serve` command reference](../reference/cli/thv_serve.md) +- [Agent Skills specification](https://agentskills.io/specification) + +## Troubleshooting + +
+Skill command fails with a connection error + +All skill commands require the ToolHive API server to be running. Start it with: + +```bash +thv serve +``` + +Then retry your skill command. + +
+ +
+Skill not discovered by your AI client + +If your AI client doesn't see an installed skill: + +1. Verify the skill is installed: + + ```bash + thv skill list + ``` + +2. Check that the skill was installed for the correct client: + + ```bash + thv skill info + ``` + +3. Verify the skill files exist in the expected directory. For Claude Code, + user-scoped skills are at `~/.claude/skills//` and project-scoped + skills are at `/.claude/skills//`. + +4. Restart your AI client to trigger skill discovery. + +
+ +
+Skill validation fails + +Run `thv skill validate` to see specific errors: + +```bash +thv skill validate ./my-skill +``` + +Common issues include: + +- Missing `SKILL.md` file in the directory +- Missing `name` or `description` in the frontmatter +- Skill `name` doesn't match the directory name +- Symlinks present in the skill directory (not allowed for security) + +
+ +
+Push to registry fails with authentication error + +Make sure you're authenticated with your container registry: + +```bash +# For GitHub Container Registry +echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin + +# For Docker Hub +docker login +``` + +Then retry the push command. + +
diff --git a/versioned_docs/version-1.0/toolhive/guides-cli/telemetry-and-metrics.mdx b/versioned_docs/version-1.0/toolhive/guides-cli/telemetry-and-metrics.mdx new file mode 100644 index 00000000..16e6335a --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-cli/telemetry-and-metrics.mdx @@ -0,0 +1,457 @@ +--- +title: Telemetry and metrics +description: + How to enable OpenTelemetry and Prometheus instrumentation for ToolHive MCP + servers. +--- + +ToolHive includes built-in instrumentation using OpenTelemetry, which gives you +comprehensive observability for your MCP server interactions. You can export +traces and metrics to popular observability backends like Jaeger, Honeycomb, +Datadog, and Grafana Cloud, or expose Prometheus metrics locally. + +## What you can monitor + +ToolHive's telemetry captures detailed information about MCP interactions +including traces, metrics, and performance data. For a comprehensive overview of +the telemetry architecture, metrics collection, and monitoring capabilities, see +the [observability overview](../concepts/observability.mdx). + +## Enable telemetry + +You can enable telemetry when running an MCP server by using the OpenTelemetry +flags with the [`thv run`](../reference/cli/thv_run.md) command or configure +defaults for all MCP servers using the +[`thv config otel`](../reference/cli/thv_config_otel.md) commands. + +### Export to an OTLP endpoint + +You can export traces and metrics to an OpenTelemetry Protocol (OTLP) compatible +backend using the `--otel-endpoint` flag. You can also specify headers for +authentication and configure sampling rates. + +This example runs the Fetch MCP server and exports traces to a local instance of +the [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/): + +```bash +thv run \ + --otel-endpoint localhost:4318 \ + --otel-service-name fetch-mcp \ + --otel-insecure \ + fetch +``` + +:::note + +The `--otel-endpoint` is provided as a hostname and optional port, without a +scheme or path (e.g., use `api.honeycomb.io` or `api.honeycomb.io:443`, not +`https://api.honeycomb.io`). ToolHive automatically uses HTTPS unless +`--otel-insecure` is specified. + +::: + +By default, the service name is set to `thv-` (e.g., `thv-fetch`), +and the sampling rate is `0.1` (10%). You can customize these settings with +additional [configuration options](#configuration-options). + +:::tip[Recommendation] + +Set the `--otel-service-name` flag to a meaningful name for each MCP server. +This helps you identify the server in your observability backend. + +::: + +### Include environment variables + +You can include specific environment variables from your host system in +telemetry spans using the `--otel-env-vars` flag. This is useful for adding +context like deployment environment or service version to your traces. + +```bash +thv run \ + --otel-endpoint api.honeycomb.io \ + --otel-headers "x-honeycomb-team=" \ + --otel-env-vars "NODE_ENV,DEPLOYMENT_ENV,SERVICE_VERSION" \ + fetch +``` + +Only the environment variables you specify will be included in spans, and +they'll appear as attributes with the `environment.` prefix (e.g., +`environment.NODE_ENV`). + +### Add custom resource attributes + +You can add custom metadata to all telemetry signals (traces and metrics) using +custom resource attributes. These attributes are useful for adding context like +team ownership, deployment region, server type, or any other metadata that helps +you filter and query your telemetry data in observability platforms. + +Custom attributes are attached globally to all telemetry signals from the MCP +server, making them available for filtering, grouping, and searching in your +observability backend. + +#### Using the CLI flag + +Use the `--otel-custom-attributes` flag to specify custom attributes as +comma-separated key=value pairs: + +```bash +thv run \ + --otel-endpoint api.honeycomb.io \ + --otel-headers "x-honeycomb-team=" \ + --otel-custom-attributes "server_type=production,region=us-east-1,team=platform" \ + fetch +``` + +Attribute keys may contain letters, numbers, dots (`.`), underscores (`_`), and +hyphens (`-`). Spaces and most other punctuation are not allowed in keys. +Attribute values can contain any UTF-8 string, including URLs. For more details, +see the +[OpenTelemetry Resource Semantic Conventions](https://opentelemetry.io/docs/specs/semconv/resource/#attributes). + +#### Using environment variables + +You can also set custom attributes using the standard OpenTelemetry environment +variable `OTEL_RESOURCE_ATTRIBUTES`: + +```bash +export OTEL_RESOURCE_ATTRIBUTES="deployment.environment=staging,service.namespace=backend" +thv run --otel-endpoint api.honeycomb.io fetch +``` + +#### Combining both methods + +When using both the CLI flag and environment variable, attributes from both +sources are combined: + +```bash +export OTEL_RESOURCE_ATTRIBUTES="deployment.environment=production" +thv run \ + --otel-endpoint api.honeycomb.io \ + --otel-custom-attributes "region=us-west-2,team=infrastructure" \ + fetch +``` + +This will add all three attributes to your telemetry data: + +- `deployment.environment=production` (from environment variable) +- `region=us-west-2` (from CLI flag) +- `team=infrastructure` (from CLI flag) + +#### Common use cases + +Custom attributes are particularly useful for: + +- **Critical context**: Add critical context to telemetry (e.g., service, host, + or environment) so data is meaningful and traceable. +- **Fast filtering and troubleshooting**: Enable fast filtering, grouping, and + correlation across distributed components during troubleshooting. +- **Consistent metadata:** Support standardized metadata via OpenTelemetry + semantic conventions for reliable observability across systems. +- **Root-cause analysis:** Pinpoint which resource is responsible for + performance issues. +- **Telemetry enrichment:** Enable automatic or manual enrichment using resource + detectors and the OpenTelemetry Collector. + +:::tip + +Choose attribute names that follow OpenTelemetry semantic conventions when +possible. For example, use `service.version` instead of `app_version` for better +compatibility with observability tools. + +::: + +### Enable Prometheus metrics + +You can expose Prometheus-style metrics at `/metrics` on the main transport port +for local scraping using the `--otel-enable-prometheus-metrics-path` flag. + +This example runs the Fetch MCP server and enables the Prometheus metrics +endpoint: + +```bash +thv run --otel-enable-prometheus-metrics-path fetch +``` + +To access the metrics, you can use `curl` or any Prometheus-compatible scraper. +The metrics are available at `http://127.0.0.1:/metrics`, where `` +is the port assigned to the MCP server. + +```bash +# Get the port number assigned to the MCP server +thv list + +# Replace with the actual port number from the output of `thv list` +curl http://127.0.0.1:/metrics +``` + +### Dual export + +You can export to both an OTLP endpoint and expose Prometheus metrics +simultaneously. + +This example exports to Honeycomb and enables the Prometheus metrics endpoint: + +```bash +thv run \ + --otel-endpoint api.honeycomb.io \ + --otel-headers "x-honeycomb-team=" \ + --otel-enable-prometheus-metrics-path \ + fetch +``` + +## Configuration options + +You can configure telemetry settings globally or per MCP server. The global +configuration is stored in the ToolHive configuration file, while per-server +configuration can be specified using command-line flags when running an MCP +server. + +### Per-server configuration + +The table below lists the available configuration options for enabling telemetry +when running an MCP server with the `thv run` command: + +```bash +thv run [--otel-endpoint ] [--otel-service-name ] \ + [--otel-metrics-enabled=] [--otel-tracing-enabled=] \ + [--otel-sampling-rate ] [--otel-headers ] \ + [--otel-custom-attributes ] [--otel-env-vars ] \ + [--otel-insecure] [--otel-enable-prometheus-metrics-path] \ + [--otel-use-legacy-attributes=] \ + +``` + +| Flag | Description | Default | +| --------------------------------------- | ------------------------------------------------------------------- | -------------- | +| `--otel-endpoint` | OTLP endpoint (e.g., `api.honeycomb.io`) | None | +| `--otel-metrics-enabled` | Enable OTLP metrics export (when OTLP endpoint is configured) | `true` | +| `--otel-tracing-enabled` | Enable distributed tracing (when OTLP endpoint is configured) | `true` | +| `--otel-service-name` | Service name for telemetry | `thv-` | +| `--otel-sampling-rate` | Trace sampling rate (0.0-1.0) | `0.1` (10%) | +| `--otel-headers` | Authentication headers in `key=value` format | None | +| `--otel-custom-attributes` | Custom resource attributes in `key=value` format | None | +| `--otel-env-vars` | List of environment variables to include in telemetry spans | None | +| `--otel-insecure` | Connect using HTTP instead of HTTPS | `false` | +| `--otel-enable-prometheus-metrics-path` | Enable `/metrics` endpoint | `false` | +| `--otel-use-legacy-attributes` | Emit legacy attribute names alongside new OTel semantic conventions | `true` | + +### Global configuration + +You can set default telemetry options for all MCP servers using the +[`thv config otel`](../reference/cli/thv_config_otel.md) command. This way you +don't have to set the same flags every time you run an MCP server. + +These defaults are applied to all MCP servers unless overridden by command-line +flags when you run a specific server. + +Currently the OpenTelemetry endpoint, sampling rate, and environment variables +can be set globally. For example, to set the default OTLP endpoint and sampling +rate: + +```bash +thv config otel set-endpoint api.honeycomb.io +thv config otel set-metrics-enabled true +thv config otel set-tracing-enabled true +thv config otel set-sampling-rate 0.25 +thv config otel set-enable-prometheus-metrics-path true +thv config otel set-insecure true +``` + +Each command has a corresponding `get` and `unset` command to retrieve or remove +the configuration. For example, to check the current OTLP endpoint: + +```bash +thv config otel get-endpoint +``` + +## Observability backends + +ToolHive can export telemetry data to many different observability backends. It +supports exporting traces and metrics to any backend that implements the OTLP +protocol. Some common examples are listed below, but specific configurations +will vary based on your environment and requirements. + +### OpenTelemetry Collector (recommended) + +The OpenTelemetry Collector is a vendor-agnostic way to receive, process and +export telemetry data. It supports many backend services, scalable deployment +options, and advanced processing capabilities. + +```mermaid +graph LR + A[ToolHive] -->|traces & metrics| B[OpenTelemetry Collector] + B --> C[AWS CloudWatch] + B --> D[Splunk] + B --> E[New Relic] + B --> F[Other OTLP backends] +``` + +You can run the OpenTelemetry Collector locally or use a managed service. For +local deployment, follow the +[OpenTelemetry Collector documentation](https://opentelemetry.io/docs/collector/). + +To export data to a local OpenTelemetry Collector, set your OTLP endpoint to the +OTLP http receiver port (default is `4318`): + +```bash +thv run \ + --otel-endpoint localhost:4318 \ + --otel-insecure \ + fetch +``` + +### Prometheus + +To collect metrics using Prometheus, run your MCP server with the +`--otel-enable-prometheus-metrics-path` flag and add the following to your +Prometheus configuration: + +```yaml title="prometheus.yml" +scrape_configs: + - job_name: 'toolhive-mcp-proxy' + static_configs: + - targets: ['localhost:'] + scrape_interval: 15s + metrics_path: /metrics +``` + +You can add multiple MCP servers to the `targets` list. Replace +`` with the port number assigned to each MCP server. + +### Jaeger + +Jaeger is a popular open-source distributed tracing system. You can run it +locally or use a managed service within your enterprise or from a third-party +provider. + +Follow the +[Jaeger getting started guide](https://www.jaegertracing.io/docs/latest/getting-started/#all-in-one) +to set up a local Jaeger instance. Once running, you can export traces to Jaeger +by setting the OTLP endpoint to Jaeger's collector: + +```bash +thv run \ + --otel-endpoint localhost:4318 \ + --otel-metrics-enabled=false \ + --otel-insecure \ + +``` + +Access the Jaeger UI at `http://localhost:16686` to view traces. + +### Honeycomb + +You can send OpenTelemetry data directly to +[Honeycomb's OTLP endpoint](https://docs.honeycomb.io/send-data/opentelemetry/#using-the-honeycomb-opentelemetry-endpoint), +or use the [OpenTelemetry Collector](#opentelemetry-collector-recommended) to +forward data to Honeycomb. This example sends data directly to Honeycomb: + +```bash +thv run \ + --otel-endpoint api.honeycomb.io \ + --otel-headers "x-honeycomb-team=" \ + --otel-service-name production-mcp-proxy \ + +``` + +You'll need your Honeycomb API key, which you can find in your +[Honeycomb account settings](https://ui.honeycomb.io/account). + +### Datadog + +Datadog has [multiple options](https://docs.datadoghq.com/opentelemetry/) for +collecting OpenTelemetry data: + +- The + [**OpenTelemetry Collector**](https://docs.datadoghq.com/opentelemetry/setup/collector_exporter/) + is recommended for existing OpenTelemetry users or users wanting a + vendor-neutral solution. + +- The [**Datadog Agent**](https://docs.datadoghq.com/opentelemetry/setup/agent) + is recommended for existing Datadog users. + +### Grafana Cloud + +You can send OpenTelemetry data to Grafana Cloud using +[Grafana Alloy](https://grafana.com/docs/opentelemetry/collector/grafana-alloy/), +Grafana Labs' supported distribution of the OpenTelemetry Collector. This is the +recommended method for production deployments. + +You can also send data directly to +[Grafana Cloud's OTLP endpoint](https://grafana.com/docs/grafana-cloud/send-data/otlp/send-data-otlp/#manual-opentelemetry-setup-for-advanced-users): + +```bash +thv run \ + --otel-endpoint otlp-gateway-prod-us-central-0.grafana.net \ + --otel-headers "Authorization=Basic $(echo -n 'user:password' | base64)" \ + +``` + +## Performance considerations + +### Sampling rates + +Adjust sampling rates based on your environment: + +- **Development**: `--otel-sampling-rate 1.0` (100% sampling) +- **Production**: `--otel-sampling-rate 0.01` (1% sampling for high-traffic + systems) +- **Default**: `--otel-sampling-rate 0.1` (10% sampling) + +### Network overhead + +Telemetry adds minimal overhead when properly configured: + +- Use appropriate sampling rates for your traffic volume +- Monitor your observability backend costs and adjust sampling accordingly + +## Related information + +- Tutorial: + [Collect telemetry for MCP workloads](../integrations/opentelemetry.mdx) +- [Telemetry and monitoring concepts](../concepts/observability.mdx) +- [`thv run` command reference](../reference/cli/thv_run.md) +- [Run MCP servers](run-mcp-servers.mdx) + +## Troubleshooting + +
+Traces not received by collector + +If traces aren't showing up in your backend: + +1. Verify the endpoint URL and authentication headers +2. Check network connectivity to the OTLP endpoint +3. Ensure sampling rate isn't too low (set to `1.0` temporarily for testing) +4. Check the ToolHive log file for errors related to telemetry (the log file + path is displayed when you start a server with `thv run`) +5. If your endpoint uses a self-signed certificate or a certificate from a + custom CA, use the `--thv-ca-bundle` flag to add your CA or self-signed + certificate. + +
+ +
+Prometheus metrics not available + +If the `/metrics` endpoint isn't accessible: + +1. Confirm `--otel-enable-prometheus-metrics-path` is set +2. Check that you're accessing the correct port +3. Verify no firewall is blocking the port + +
+ +
+High CPU or memory usage + +If telemetry is consuming too many resources on your system: + +1. Reduce the sampling rate with `--otel-sampling-rate` for specific servers or + globally with `thv config otel set-sampling-rate` +2. Only enable telemetry for servers you need to monitor +3. Monitor the resource usage of the OpenTelemetry Collector or other backend + services + +
diff --git a/versioned_docs/version-1.0/toolhive/guides-cli/test-mcp-servers.mdx b/versioned_docs/version-1.0/toolhive/guides-cli/test-mcp-servers.mdx new file mode 100644 index 00000000..37b70a69 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-cli/test-mcp-servers.mdx @@ -0,0 +1,95 @@ +--- +title: Test MCP servers +description: Learn how to test and validate MCP servers using the ToolHive CLI. +--- + +ToolHive includes several commands to help you test and validate MCP servers. +These commands ensure that your servers are functioning correctly. This is +useful for: + +- Validating new MCP server installations +- Troubleshooting existing MCP servers +- Testing custom MCP servers during development + +## MCP Inspector + +The [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector) is an +interactive tool for testing and debugging MCP servers. + +You can quickly launch the MCP Inspector and connect it to an MCP server running +with ToolHive using the `thv inspector` command: + +```bash +thv inspector +``` + +ToolHive downloads and runs the official Inspector container image and outputs +the URL to open it in your web browser with the target MCP server's details +pre-populated. + +Example: + +```bash +thv run fetch +thv inspector fetch +``` + +The output will look like this: + +```text +2:52PM INFO Pulling image: ghcr.io/modelcontextprotocol/inspector:0.17.0 +2:52PM INFO Pull complete for image: ghcr.io/modelcontextprotocol/inspector:0.17.0 +2:52PM INFO Waiting for MCP Inspector to be ready... +2:52PM INFO Connected to MCP server: fetch +2:52PM INFO Inspector UI is now available at http://localhost:6274?transport=streamable-http&serverUrl=http://host.docker.internal:21309/mcp&MCP_PROXY_AUTH_TOKEN= +``` + +Open the provided URL in your web browser to access the MCP Inspector UI, where +you can interactively test the MCP server's tools, prompts, and resources. + +## `thv mcp` commands + +ToolHive also includes several +[`thv mcp` commands](../reference/cli/thv_mcp_list.md) to test and validate MCP +servers. You can list the MCP server's tools, prompts, and resources: + +```bash +thv mcp list tools --server +thv mcp list prompts --server +thv mcp list resources --server +``` + +Replace `` with either the MCP server name or its ToolHive +proxy URL. You can find both values in the output of `thv list`. + +Add the `--format json` flag to get the output in JSON format. + +:::note + +Currently, the `thv mcp` commands only support using server names for local MCP +servers. For remote servers, you must use the full URL instead of the server +name. You can track +[this issue on GitHub](https://github.com/stacklok/toolhive/issues/2035). + +To use URLs instead of names, get the server URL from `thv list` and use it +directly, like `thv mcp list tools --server http://localhost:12345/mcp`. + +::: + +## ToolHive playground + +While the MCP Inspector and `thv mcp` commands are great for basic functional +testing and validating spec compliance, you may want to experiment with MCP +servers in a more interactive way. It's also important to see how MCP servers +perform in real-world scenarios with actual AI models. + +The _playground_ in the ToolHive UI lets you test different tools interactively +and see how your MCP server responds to various prompts. + +See [Test MCP servers in the ToolHive UI](../guides-ui/playground.mdx) to learn +about the playground's features and how to get started. + +## Related information + +- [`thv inspector` command reference](../reference/cli/thv_inspector.md) +- [`thv mcp list` command reference](../reference/cli/thv_mcp_list.md) diff --git a/versioned_docs/version-1.0/toolhive/guides-cli/thvignore.mdx b/versioned_docs/version-1.0/toolhive/guides-cli/thvignore.mdx new file mode 100644 index 00000000..90f50ba3 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-cli/thvignore.mdx @@ -0,0 +1,136 @@ +--- +title: Hide sensitive files +description: + Use .thvignore to prevent secrets from leaking into MCP containers while + keeping fast bind mounts for development. +--- + +Some MCP servers need access to your project files, but you don't want to expose +secrets like `.env`, SSH keys, or cloud credentials. ToolHive supports a +`.thvignore` mechanism that masks selected paths from the container while +keeping all other files available through a live bind mount for a smooth +developer experience. + +## How it works + +When you mount a directory and a `.thvignore` file is present at the mount +source, ToolHive resolves the ignore patterns and overlays those paths inside +the container: + +- Directories (for example, `.ssh/`, `node_modules/`): overlaid using a tmpfs + mount at the container path +- Files (for example, `.env`, `secrets.json`): overlaid using a bind mount of a + shared, empty host file at the container path + +The rest of the files remain bind-mounted from your host, so edits are visible +in the container immediately. + +## Create an ignore file + +Create a file named `.thvignore` at the root of the directory you intend to +mount. Use simple, gitignore-like patterns: + +```text +# secrets +.env +.env.* +*.key +*.pem + +# cloud credentials +.aws/ +.gcp/ + +# SSH keys +.ssh/ + +# OS junk +.DS_Store +``` + +Guidelines: + +- `dir/` matches a directory directly under the mount source +- `file.ext` matches a file directly under the mount source +- `*.ext` matches any file with that extension directly under the mount source +- Lines starting with `#` are comments; blank lines are ignored + +:::info[Pattern matching] + +ToolHive uses simple gitignore-like patterns. Advanced gitignore glob syntax +like `**/*.env` (to match files in any subdirectory) is not currently supported. +Patterns only match files and directories directly under the mount source. + +::: + +## Run a server with .thvignore + +Mount your project directory as usual. ToolHive automatically reads `.thvignore` +if present: + +```bash +thv run --volume ./my-project:/projects filesystem +``` + +To print resolved overlay targets for debugging: + +```bash +thv run --volume ./my-project:/projects \ + --print-resolved-overlays \ + filesystem +``` + +The resolved overlays are logged to the workload's log file. For a complete list +of options, see the [`thv run` command reference](../reference/cli/thv_run.md). + +## Global ignore patterns + +You can define global ignore patterns in a platform-specific location: + +- **Linux**: `~/.config/toolhive/thvignore` +- **macOS**: `~/Library/Application Support/toolhive/thvignore` +- **Windows**: `%LOCALAPPDATA%\toolhive\thvignore` + +Patterns contained in the global configuration are loaded in addition to a local +`.thvignore` file. To disable global patterns for a specific workload, use the +`--ignore-globally=false` option: + +```bash +thv run --ignore-globally=false --volume ./my-project:/projects filesystem +``` + +:::tip[Recommendation] + +Set machine-wide patterns (for example, `.aws/`, `.gcp/`, `.ssh/`, `*.pem`, +`.docker/config.json`) in the global file, and keep app-specific patterns (for +example, `.env*`, build artifacts) in your project's local `.thvignore`. + +::: + +## Troubleshooting + +
+Overlays didn't apply + +- Ensure `.thvignore` exists in the mount source directory (not elsewhere) +- Confirm patterns match actual names relative to the mount source +- Run with `--print-resolved-overlays` and check the workload's log file path + displayed by `thv run` + +
+ +
+Can't list a parent directory + +- On SELinux systems, listing a parent directory may fail even though specific + files are accessible. Probe individual paths instead (for example, `stat` or + `cat`). + +
+ +## Related information + +- [File system access](./filesystem-access.mdx) +- [Run MCP servers](./run-mcp-servers.mdx) +- [Network isolation](./network-isolation.mdx) +- [`thv run` command reference](../reference/cli/thv_run.md) diff --git a/versioned_docs/version-1.0/toolhive/guides-cli/token-exchange.mdx b/versioned_docs/version-1.0/toolhive/guides-cli/token-exchange.mdx new file mode 100644 index 00000000..72a86409 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-cli/token-exchange.mdx @@ -0,0 +1,210 @@ +--- +title: Configure token exchange for backend authentication +description: + How to set up token exchange so MCP servers can authenticate to backend + services using ToolHive. +--- + +This guide shows you how to configure token exchange, which allows MCP servers +to authenticate to backend APIs using short-lived, properly scoped tokens +instead of embedded secrets. + +For conceptual background on how token exchange works and its security benefits, +see [Backend authentication](../concepts/backend-auth.mdx), which includes a +[sequence diagram](../concepts/backend-auth.mdx#same-idp-with-token-exchange) +illustrating the complete flow. + +## Prerequisites + +Before you begin, make sure you have: + +- ToolHive installed and working +- Basic familiarity with OAuth, OIDC, and JWT concepts +- An identity provider that supports + [RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) token exchange (such + as Okta, Auth0, or Keycloak) +- A backend service configured to accept tokens from your identity provider +- Familiarity with [Authentication and authorization](./auth.mdx) setup in + ToolHive + +From your identity provider, you'll need: + +- Audience value for the MCP server +- Issuer URL +- JWKS URL (for key verification) +- Token exchange endpoint URL +- Client credentials for the token exchange client + +## Configure your identity provider + +Token exchange requires your identity provider to issue tokens for the backend +service when presented with a valid MCP server token. The exact configuration +steps vary by provider, but generally include: + +### Register a token exchange client + +Create an OAuth application in your identity provider for ToolHive to use when +performing token exchange: + +- Note the client ID and client secret +- Grant the application permission to use the + `urn:ietf:params:oauth:grant-type:token-exchange` grant type + +Token exchange is an authenticated flow—ToolHive uses these credentials to prove +its identity when requesting exchanged tokens from the identity provider. + +:::tip[Okta] + +Create an API Services application for ToolHive and enable the token exchange +grant type in the application settings. + +::: + +### Define audience and scopes for the backend service + +Configure your identity provider to recognize the backend service: + +- Define the audience value that identifies your backend service (for example, + `backend-api`) +- Specify the scopes the backend service accepts (for example, `api:read`, + `api:write`) + +:::tip[Okta] + +Create a custom authorization server for the backend service and define the +scopes under **Security > API > Authorization Servers**. + +::: + +### Create an access policy + +Set up a policy that permits token exchange and controls what scopes are +included in exchanged tokens: + +- Enable the token exchange grant type for the ToolHive client +- Define which users or groups can obtain tokens for the backend service +- Specify the scopes included in exchanged tokens + +:::tip[Okta] + +Add a trust relationship from the MCP authorization server to the backend +authorization server, then create access policies on the backend server to +permit token exchange. + +Consult the +[Okta token exchange documentation](https://developer.okta.com/docs/guides/set-up-token-exchange/main/) +for detailed steps. + +::: + +## MCP server requirements + +The MCP server that ToolHive fronts must accept a per-request authentication +token. Specifically, the server should: + +- Read the access token from the `Authorization: Bearer` header (or a custom + header if you configure `--token-exchange-header-name`) +- Use this token to authenticate to the backend service +- Not rely on hardcoded secrets or environment variables for backend + authentication + +ToolHive injects the exchanged token into each request, so the MCP server +receives a fresh, properly scoped token for every call. + +:::tip[Example] + +The [Apollo MCP server](https://github.com/apollographql/apollo-mcp-server) +supports token passthrough via its config +(`disable_auth_token_passthrough: false`). + +::: + +## Run an MCP server with token exchange + +Once your identity provider is configured, start your MCP server with token +exchange enabled: + +```bash +thv run \ + --oidc-audience \ + --oidc-issuer \ + --oidc-jwks-url \ + --token-exchange-url \ + --token-exchange-client-id \ + --token-exchange-client-secret \ + --token-exchange-audience \ + --token-exchange-scopes \ + +``` + +### Parameter reference + +| Parameter | Description | +| -------------------------------- | ------------------------------------------------------------------------- | +| `--oidc-*` | Standard OIDC parameters for validating incoming client tokens | +| `--token-exchange-url` | Your identity provider's token exchange endpoint | +| `--token-exchange-client-id` | Client ID for ToolHive to authenticate to the IdP | +| `--token-exchange-client-secret` | Client secret for ToolHive (or use `--token-exchange-client-secret-file`) | +| `--token-exchange-audience` | Target audience for the exchanged token (your backend service) | +| `--token-exchange-scopes` | Scopes to request for the backend service | + +Optional parameters: + +- `--token-exchange-header-name`: Custom header name for injecting the exchanged + token (defaults to replacing the `Authorization` header) +- `--token-exchange-subject-token-type`: Type of subject token to exchange; use + `id_token` for Google STS (defaults to `access_token`) + +## Verify the configuration + +To confirm token exchange is working: + +1. Connect to the MCP server with a client that supports authentication +2. Make a tool call that requires backend access +3. Check that the request succeeds and the backend receives a properly scoped + token + +You can also verify by examining your identity provider's logs for successful +token exchange requests, or by checking audit logs on your backend service to +confirm requests arrive with the correct user identity and scopes. + +## Example: Okta configuration + +This example shows a complete configuration for an MCP server that connects to a +backend API, using Okta for token exchange: + +```bash +thv run \ + --oidc-audience mcp-server \ + --oidc-issuer https://dev-123456.okta.com/oauth2/aus1234567890 \ + --oidc-jwks-url https://dev-123456.okta.com/oauth2/aus1234567890/v1/keys \ + --token-exchange-url https://dev-123456.okta.com/oauth2/aus9876543210/v1/token \ + --token-exchange-client-id 0oa0987654321fedcba \ + --token-exchange-client-secret-file /path/to/client-secret \ + --token-exchange-audience backend-api \ + --token-exchange-scopes "api:read,api:write" \ + +``` + +Key points in this example: + +- **Two authorization servers**: The `--oidc-issuer` URL (`aus1234567890`) is + the MCP authorization server that validates incoming client tokens. The + `--token-exchange-url` uses a different authorization server (`aus9876543210`) + that issues tokens for the backend API. +- **Audience transformation**: Client tokens arrive with audience `mcp-server`. + ToolHive exchanges them for tokens with audience `backend-api`, which the + backend service expects. +- **Scope transformation**: The original token has MCP-specific scopes (like + `mcp:tools:call`), while the exchanged token has backend-specific scopes + (`api:read`, `api:write`). The user's identity is preserved, but the + permissions are transformed for the target service. + +## Related information + +- [Backend authentication](../concepts/backend-auth.mdx) - conceptual overview + of token exchange and federation +- [Authentication and authorization](./auth.mdx) - basic auth setup for MCP + servers +- [CLI reference for `thv run`](../reference/cli/thv_run.md) - complete list of + flags diff --git a/versioned_docs/version-1.0/toolhive/guides-k8s/auth-k8s.mdx b/versioned_docs/version-1.0/toolhive/guides-k8s/auth-k8s.mdx new file mode 100644 index 00000000..3195b8fa --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-k8s/auth-k8s.mdx @@ -0,0 +1,869 @@ +--- +title: Authentication and authorization +description: + How to set up authentication and authorization for MCP servers in Kubernetes + using the ToolHive Operator. +--- + +import OidcPrerequisites from '../_partials/_oidc-prerequisites.mdx'; +import BasicCedarConfig from '../_partials/_basic-cedar-config.mdx'; +import AuthTroubleshooting from '../_partials/_auth-troubleshooting.mdx'; + +This guide shows you how to secure your MCP servers in Kubernetes using +authentication and authorization with the ToolHive Operator. + +:::info + +Authentication and authorization are emerging capabilities in the MCP ecosystem. +The official MCP authorization specification is still evolving, and client +support for these features is limited. ToolHive is leading the way in +implementing these capabilities, but you may encounter some limitations with +certain clients. + +::: + +## Prerequisites + +You'll need: + +- Kubernetes cluster with RBAC enabled +- ToolHive Operator installed (see + [Deploy the ToolHive Operator](./deploy-operator.mdx)) +- `kubectl` access to your cluster + +## Choose your authentication approach + +There are four main ways to authenticate with MCP servers running in Kubernetes: + +### Approach 1: External identity provider authentication + +Use this when you want to authenticate users or external services using +providers like Google, GitHub, Microsoft Entra ID, Okta, or Auth0. + +**Prerequisites for external IdP:** + + + +### Approach 2: Shared OIDC configuration with ConfigMap + +Use this when you want to share the same OIDC configuration across multiple +MCPServers. This is ideal for managing multiple servers with the same external +identity provider. + +**Prerequisites for shared OIDC:** + +- External identity provider configured (same as Approach 1) +- Understanding of Kubernetes ConfigMaps + +### Approach 3: Kubernetes service-to-service authentication + +Use this when you have client applications running in the same Kubernetes +cluster that need to call MCP servers. This approach uses Kubernetes service +account tokens for authentication. + +**Prerequisites for service-to-service:** + +- Client applications running in Kubernetes pods +- Understanding of Kubernetes service accounts and RBAC + +### Approach 4: Embedded authorization server authentication + +Use this when you want ToolHive to handle the full OAuth flow, including +redirecting users to an upstream identity provider for authentication. This +approach is ideal for MCP servers that accept `Authorization: Bearer` tokens. + +For conceptual background, see +[Embedded authorization server](../concepts/auth-framework.mdx#embedded-authorization-server). + +**Prerequisites for embedded authorization server:** + +- An upstream identity provider that supports the OAuth 2.0 authorization code + flow (such as Okta, Microsoft Entra ID, Auth0, or any OIDC-compliant provider) +- A registered OAuth application/client with your upstream provider +- Client ID and client secret from your upstream provider + +## Set up external identity provider authentication + +**Step 1: Create an MCPServer with external OIDC** + +Create an `MCPServer` resource configured to accept tokens from your external +identity provider. The ToolHive proxy will handle authentication before +forwarding requests to the MCP server. + +```yaml title="mcp-server-external-auth.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: weather-server-external + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/weather-mcp/server + transport: sse + port: 8080 + permissionProfile: + type: builtin + name: network + # Authentication configuration for external IdP + oidcConfig: + type: inline + inline: + issuer: 'https://your-oidc-issuer.com' + audience: 'your-audience' + clientId: 'your-client-id' + jwksUrl: 'https://your-oidc-issuer.com/path/to/jwks' + resources: + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' +``` + +Replace the OIDC placeholders with your actual identity provider configuration. + +**Step 2: Apply the MCPServer resource** + +```bash +kubectl apply -f mcp-server-external-auth.yaml +``` + +**Step 3: Test external authentication** + +Clients connecting to this MCP server must include a valid JWT token from your +configured identity provider in their requests. The ToolHive proxy will validate +the token before allowing access to the MCP server. + +:::note[Obtaining JWT tokens] + +How to obtain JWT tokens varies by identity provider and is outside the scope of +this guide. Consult your identity provider's documentation for specific +instructions on: + +- Interactive user authentication flows (OAuth 2.0 Authorization Code flow) +- Service-to-service authentication (Client Credentials flow) +- API token generation and management + +For Kubernetes service accounts, tokens are automatically mounted at +`/var/run/secrets/kubernetes.io/serviceaccount/token` in pods. + +::: + +## Set up shared OIDC configuration with ConfigMap + +**Step 1: Create OIDC ConfigMap** + +Create a ConfigMap containing the OIDC configuration: + +```yaml title="shared-oidc-config.yaml" +apiVersion: v1 +kind: ConfigMap +metadata: + name: shared-oidc-config + namespace: toolhive-system +data: + issuer: 'https://auth.example.com' + audience: 'https://mcp.example.com' + clientId: 'shared-client-id' + jwksUrl: 'https://auth.example.com/.well-known/jwks.json' +``` + +```bash +kubectl apply -f shared-oidc-config.yaml +``` + +**Step 2: Reference ConfigMap in MCPServer** + +Create MCPServer resources that reference the shared configuration: + +```yaml title="mcp-server-with-configmap-oidc.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: weather-server-shared-oidc + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/weather-mcp/server + transport: sse + port: 8080 + permissionProfile: + type: builtin + name: network + # Reference shared OIDC configuration + oidcConfig: + type: configMap + configMap: + name: shared-oidc-config + resources: + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' +``` + +```bash +kubectl apply -f mcp-server-with-configmap-oidc.yaml +``` + +### Benefits of ConfigMap approach + +- **Centralized management**: Update OIDC settings in one place +- **Consistency**: Ensure all MCPServers use identical authentication config +- **GitOps friendly**: Manage configuration separately from MCPServer resources +- **Multi-server deployments**: Deploy multiple servers with same auth easily + +## Set up Kubernetes service-to-service authentication + +This approach is ideal when you have client applications running in the same +Kubernetes cluster that need to call MCP servers. + +**Step 1: Create service account for client application** + +Create a service account that your client application will use: + +```yaml title="client-service-account.yaml" +apiVersion: v1 +kind: ServiceAccount +metadata: + name: mcp-client + namespace: client-apps +``` + +```bash +kubectl apply -f client-service-account.yaml +``` + +**Step 2: Create MCPServer for service-to-service auth** + +Create an `MCPServer` resource configured to accept Kubernetes service account +tokens: + +```yaml title="mcp-server-k8s-auth.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: weather-server-k8s + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/weather-mcp/server + transport: sse + port: 8080 + permissionProfile: + type: builtin + name: network + # Authentication configuration for Kubernetes service accounts + oidcConfig: + type: kubernetes + kubernetes: + serviceAccount: 'mcp-client' + namespace: 'client-apps' + audience: 'toolhive' + issuer: 'https://kubernetes.default.svc' + jwksUrl: 'https://kubernetes.default.svc/openid/v1/jwks' + resources: + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' +``` + +This configuration only allows requests from pods using the `mcp-client` service +account in the `client-apps` namespace. + +```bash +kubectl apply -f mcp-server-k8s-auth.yaml +``` + +**Step 3: Deploy client application with service account** + +Deploy your client application using the service account: + +```yaml title="client-app.yaml" +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mcp-client-app + namespace: client-apps +spec: + replicas: 1 + selector: + matchLabels: + app: mcp-client-app + template: + metadata: + labels: + app: mcp-client-app + spec: + serviceAccountName: mcp-client + containers: + - name: client + image: your-client-app:latest + env: + - name: MCP_SERVER_URL + value: 'http://weather-server-k8s.toolhive-system.svc.cluster.local:8080' +``` + +```bash +kubectl apply -f client-app.yaml +``` + +Your client application can now authenticate to the MCP server using its +Kubernetes service account token, which is automatically mounted at +`/var/run/secrets/kubernetes.io/serviceaccount/token`. + +## Set up embedded authorization server authentication + +The embedded authorization server runs an OAuth authorization server within the +ToolHive proxy. It handles the full OAuth flow by redirecting users to your +upstream identity provider for authentication, then issuing JWTs that the proxy +validates on subsequent requests. This provides MCP servers with +`Authorization: Bearer` tokens without requiring separate authorization server +infrastructure. + +This setup uses the `MCPExternalAuthConfig` custom resource, following the same +pattern as [token exchange configuration](./token-exchange-k8s.mdx). + +**Step 1: Create a Secret for the upstream provider client credentials** + +Store the OAuth client secret for your upstream identity provider: + +```yaml title="upstream-idp-secret.yaml" +apiVersion: v1 +kind: Secret +metadata: + name: upstream-idp-secret + namespace: toolhive-system +type: Opaque +stringData: + client-secret: '' +``` + +```bash +kubectl apply -f upstream-idp-secret.yaml +``` + +**Step 2: Create a Secret for JWT signing keys** + +The embedded authorization server signs JWTs with a private key you provide. +Generate a PEM-encoded private key (RSA or EC), for example: + +```bash +openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out signing-key.pem +``` + +Then create a Secret containing the key: + +```yaml title="auth-server-signing-key.yaml" +apiVersion: v1 +kind: Secret +metadata: + name: auth-server-signing-key + namespace: toolhive-system +type: Opaque +stringData: + signing-key: | + -----BEGIN PRIVATE KEY----- + + -----END PRIVATE KEY----- +``` + +```bash +kubectl apply -f auth-server-signing-key.yaml +``` + +:::tip[Key rotation] + +For key rotation, you can reference multiple signing key Secrets in the +`signingKeySecretRefs` list. The first key is used for signing new tokens. +Additional keys are used for verification only, so tokens signed before rotation +remain valid. + +::: + +**Step 3: Create a Secret for HMAC keys** + +The embedded authorization server uses a symmetric HMAC key to sign +authorization codes and refresh tokens. The key must be at least 32 bytes and +cryptographically random, for example: + +```bash +openssl rand -base64 32 +``` + +```yaml title="auth-server-hmac-secret.yaml" +apiVersion: v1 +kind: Secret +metadata: + name: auth-server-hmac-secret + namespace: toolhive-system +type: Opaque +stringData: + hmac-key: '' +``` + +```bash +kubectl apply -f auth-server-hmac-secret.yaml +``` + +:::warning[Ephemeral keys for development only] + +If you omit the `signingKeySecretRefs` and `hmacSecretRefs` fields, ToolHive +generates ephemeral keys that are lost on pod restart. All previously issued +tokens become invalid after a restart. Only omit these Secrets for development +and testing. + +::: + +**Step 4: Create the MCPExternalAuthConfig resource** + +Create an `MCPExternalAuthConfig` resource with the `embeddedAuthServer` type. +This example configures an OIDC upstream provider (the most common case): + +```yaml title="embedded-auth-config.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPExternalAuthConfig +metadata: + name: embedded-auth-server + namespace: toolhive-system +spec: + type: embeddedAuthServer + embeddedAuthServer: + issuer: 'https://mcp.example.com' + signingKeySecretRefs: + - name: auth-server-signing-key + key: signing-key + hmacSecretRefs: + - name: auth-server-hmac-secret + key: hmac-key + tokenLifespans: + accessTokenLifespan: '1h' + refreshTokenLifespan: '168h' + authCodeLifespan: '10m' + upstreamProviders: + - name: google + type: oidc + oidcConfig: + issuerUrl: 'https://accounts.google.com' + clientId: '' + clientSecretRef: + name: upstream-idp-secret + key: client-secret + scopes: + - openid + - profile + - email +``` + +```bash +kubectl apply -f embedded-auth-config.yaml +``` + +**Configuration reference:** + +| Field | Description | +| ---------------------- | ---------------------------------------------------------------------------------------------------------------------- | +| `issuer` | HTTPS URL identifying this authorization server. Appears in the `iss` claim of issued JWTs. | +| `signingKeySecretRefs` | References to Secrets containing JWT signing keys. First key is active; additional keys support rotation. | +| `hmacSecretRefs` | References to Secrets with symmetric keys for signing authorization codes and refresh tokens. | +| `tokenLifespans` | Configurable durations for access tokens (default: 1h), refresh tokens (default: 168h), and auth codes (default: 10m). | +| `upstreamProviders` | Configuration for the upstream identity provider. Currently supports one provider. | + +**Step 5: Create the MCPServer resource** + +The MCPServer needs two configuration references: `externalAuthConfigRef` +enables the embedded authorization server, and `oidcConfig` validates the JWTs +that the embedded authorization server issues. Unlike approaches 1–3 where +`oidcConfig` points to an external identity provider, here it points to the +embedded authorization server itself—the `oidcConfig` issuer must match the +`issuer` in your `MCPExternalAuthConfig`. + +```yaml title="mcp-server-embedded-auth.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: weather-server-embedded + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/weather-mcp/server + transport: streamable-http + proxyPort: 8080 + permissionProfile: + type: builtin + name: network + # Reference the embedded authorization server configuration + externalAuthConfigRef: + name: embedded-auth-server + # Validate JWTs issued by the embedded authorization server + oidcConfig: + type: inline + resourceUrl: 'https://mcp.example.com/mcp' + inline: + # This must match the embedded authorization server issuer url + issuer: 'https://mcp.example.com' + resources: + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' +``` + +```bash +kubectl apply -f mcp-server-embedded-auth.yaml +``` + +:::note + +The `oidcConfig` issuer must match the `issuer` in your `MCPExternalAuthConfig`. +The embedded authorization server exposes a JWKS endpoint that the proxy uses to +validate the JWTs it issues. The proxy also exposes OAuth discovery endpoints +(`/.well-known/oauth-authorization-server`) so MCP clients can discover the +authorization endpoints automatically. + +::: + +### Configure session storage + +By default, the embedded authorization server stores sessions in memory. +Upstream tokens are lost when pods restart, requiring users to re-authenticate. +For production deployments, configure Redis Sentinel as the storage backend by +adding a `storage` block to your `MCPExternalAuthConfig`: + +```yaml title="storage block for MCPExternalAuthConfig" +storage: + type: redis + redis: + sentinelConfig: + masterName: mymaster + sentinelService: + name: redis-sentinel + namespace: redis + aclUserConfig: + usernameSecretRef: + name: redis-acl-secret + key: username + passwordSecretRef: + name: redis-acl-secret + key: password +``` + +Create the Secret containing your Redis ACL credentials: + +```bash +kubectl create secret generic redis-acl-secret \ + --namespace toolhive-system \ + --from-literal=username=toolhive-auth \ + --from-literal=password="YOUR_REDIS_ACL_PASSWORD" +``` + +For a complete walkthrough including deploying Redis Sentinel from scratch, see +[Redis Sentinel session storage](./redis-session-storage.mdx). + +### Using an OAuth 2.0 upstream provider + +If your upstream identity provider does not support OIDC discovery, you can +configure it as an OAuth 2.0 provider with explicit endpoints. This is useful +for providers like GitHub that use OAuth 2.0 but don't implement the full OIDC +specification. + +```yaml title="embedded-auth-oauth2-config.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPExternalAuthConfig +metadata: + name: embedded-auth-oauth2 + namespace: toolhive-system +spec: + type: embeddedAuthServer + embeddedAuthServer: + issuer: 'https://mcp.example.com' + signingKeySecretRefs: + - name: auth-server-signing-key + key: signing-key + hmacSecretRefs: + - name: auth-server-hmac-secret + key: hmac-key + upstreamProviders: + - name: github + type: oauth2 + oauth2Config: + authorizationEndpoint: 'https://github.com/login/oauth/authorize' + tokenEndpoint: 'https://github.com/login/oauth/access_token' + userInfo: + endpointUrl: 'https://api.github.com/user' + httpMethod: GET + additionalHeaders: + Accept: 'application/vnd.github+json' + fieldMapping: + subjectFields: + - id + - login + nameFields: + - name + - login + emailFields: + - email + clientId: '' + clientSecretRef: + name: upstream-idp-secret + key: client-secret + scopes: + - user:email + - read:user +``` + +:::note + +OAuth 2.0 providers require explicit endpoint configuration and a `userInfo` +section, unlike OIDC providers which auto-discover these from the issuer URL. +The `fieldMapping` section maps provider-specific response fields to standard +user identity fields. For example, GitHub returns `login` instead of the +standard `name` field. + +::: + +## Set up authorization + +All authentication approaches can use the same authorization configuration using +Cedar policies. + +**Step 1: Create authorization configuration** + + + +**Step 2: Create a ConfigMap with policies** + +Store your authorization configuration in a ConfigMap: + +```yaml title="authz-configmap.yaml" +apiVersion: v1 +kind: ConfigMap +metadata: + name: authz-config + namespace: toolhive-system +data: + authz-config.json: | + { + "version": "1.0", + "type": "cedarv1", + "cedar": { + "policies": [ + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"weather\");", + "permit(principal == Client::\"alice123\", action == Action::\"call_tool\", resource == Tool::\"admin_tool\");", + "permit(principal, action == Action::\"call_tool\", resource) when { principal.claim_roles.contains(\"premium\") };" + ], + "entities_json": "[]" + } + } +``` + +```bash +kubectl apply -f authz-configmap.yaml +``` + +**Step 3: Update MCPServer to use authorization** + +Add the authorization configuration to your `MCPServer` resources: + +```yaml title="mcp-server-with-authz.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: weather-server-with-authz + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/weather-mcp/server + transport: sse + port: 8080 + permissionProfile: + type: builtin + name: network + # Authentication configuration + oidcConfig: + type: kubernetes + kubernetes: + serviceAccount: 'mcp-client' + namespace: 'client-apps' + audience: 'toolhive' + issuer: 'https://kubernetes.default.svc' + jwksUrl: 'https://kubernetes.default.svc/openid/v1/jwks' + # Authorization configuration + authzConfig: + type: configMap + configMap: + name: authz-config + key: authz-config.json + resources: + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' +``` + +```bash +kubectl apply -f mcp-server-with-authz.yaml +``` + +## Test your setup + +### Test external IdP authentication + +1. Deploy the external IdP configuration +2. Obtain a valid JWT token from your identity provider +3. Make a request to the MCP server including the token + +### Test service-to-service authentication + +1. Deploy both the MCP server and client application +2. Check that the client can successfully call the MCP server +3. Verify authentication in the ToolHive proxy logs: + +```bash +kubectl logs -n toolhive-system -l app.kubernetes.io/name=weather-server-k8s +``` + +### Test embedded authorization server authentication + +1. Deploy the `MCPExternalAuthConfig` and `MCPServer` resources +2. Check that the MCPServer is running: + + ```bash + kubectl get mcpserver -n toolhive-system weather-server-embedded + ``` + +3. If the server is exposed outside the cluster, verify the OAuth discovery + endpoint is available: + + ```bash + curl https:///.well-known/oauth-authorization-server + ``` + +4. Connect with an MCP client that supports the MCP OAuth specification. The + client should be redirected to your upstream identity provider for + authentication. +5. Check the proxy logs for successful authentication: + + ```bash + kubectl logs -n toolhive-system \ + -l app.kubernetes.io/name=weather-server-embedded + ``` + +### Test authorization + +1. Make requests that should be permitted by your policies +2. Make requests that should be denied +3. Check the proxy logs to see authorization decisions + +## Related information + +- For conceptual understanding, see + [Authentication and authorization framework](../concepts/auth-framework.mdx) +- For conceptual background on the embedded authorization server, see + [Embedded authorization server](../concepts/auth-framework.mdx#embedded-authorization-server) +- For a similar configuration pattern using token exchange, see + [Configure token exchange](./token-exchange-k8s.mdx) +- For detailed Cedar policy syntax, see + [Cedar policies](../concepts/cedar-policies.mdx) and the + [Cedar documentation](https://docs.cedarpolicy.com/) +- For running MCP servers without authentication, see + [Run MCP servers in Kubernetes](./run-mcp-k8s.mdx) +- For ToolHive Operator installation, see + [Deploy the ToolHive Operator](./deploy-operator.mdx) + +## Troubleshooting + + + +
+Kubernetes-specific issues + +**MCPServer resource issues:** + +- Check the MCPServer status: `kubectl get mcpserver -n toolhive-system` +- Describe the resource for details: + `kubectl describe mcpserver weather-server-k8s -n toolhive-system` + +**Service account issues:** + +- Verify the service account exists: `kubectl get sa -n client-apps mcp-client` +- Check RBAC permissions if needed + +**ConfigMap mounting issues:** + +- Verify the ConfigMap exists: + `kubectl get configmap -n toolhive-system authz-config` +- Check the ConfigMap content: + `kubectl get configmap authz-config -n toolhive-system -o yaml` + +**OIDC configuration issues:** + +- For external IdP: Ensure the issuer URL is accessible from within the cluster +- For Kubernetes auth: Ensure the Kubernetes API server has OIDC enabled +- Check that the JWKS URL returns valid keys + +**Network connectivity:** + +- Verify pods can reach the Kubernetes API server +- Check cluster DNS resolution +- Test service-to-service connectivity: + `kubectl exec -n client-apps deployment/mcp-client-app -- curl http://weather-server-k8s.toolhive-system.svc.cluster.local:8080` + +**ToolHive Operator issues:** + +- Check operator logs: + `kubectl logs -n toolhive-system -l app.kubernetes.io/name=toolhive-operator` +- Verify the operator is running: `kubectl get pods -n toolhive-system` + +
+ +
+Embedded authorization server issues + +**OAuth flow not initiating:** + +- Verify the `MCPExternalAuthConfig` resource exists in the same namespace: + `kubectl get mcpexternalauthconfig -n toolhive-system` +- Check that the `externalAuthConfigRef.name` in your `MCPServer` matches the + `MCPExternalAuthConfig` resource name +- Verify the upstream provider's client ID and redirect URI are correctly + configured in the `MCPExternalAuthConfig` + +**Token validation failures after restart:** + +- Ensure you have configured `signingKeySecretRefs` and `hmacSecretRefs` with + persistent keys +- Without these, ephemeral keys are generated on startup, invalidating all + previously issued tokens + +**Upstream IdP redirect errors:** + +- Verify the redirect URI configured in your upstream provider matches the + ToolHive proxy's callback URL (typically + `https:///oauth/callback`) +- Check that the upstream provider's issuer URL is accessible from within the + cluster +- For OIDC providers, ensure the `/.well-known/openid-configuration` endpoint is + reachable from the proxy pod + +**JWT signing key issues:** + +- Verify signing key Secrets exist: + `kubectl get secret -n toolhive-system auth-server-signing-key` +- Ensure the key format is correct (PEM-encoded RSA or EC private key) +- Check proxy logs for key loading errors: + `kubectl logs -n toolhive-system -l app.kubernetes.io/name=weather-server-embedded` + +**OIDC configuration mismatch:** + +- Ensure the `oidcConfig.inline.issuer` on your `MCPServer` matches the `issuer` + in your `MCPExternalAuthConfig` +- Verify the `resourceUrl` in `oidcConfig` matches the external URL of the MCP + server + +
diff --git a/versioned_docs/version-1.0/toolhive/guides-k8s/connect-clients.mdx b/versioned_docs/version-1.0/toolhive/guides-k8s/connect-clients.mdx new file mode 100644 index 00000000..9ba6ed51 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-k8s/connect-clients.mdx @@ -0,0 +1,1102 @@ +--- +title: Connect clients to MCP servers +description: + Learn how to connect clients to your Kubernetes-hosted MCP servers in + ToolHive. +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Overview + +After deploying MCP servers in your Kubernetes cluster, you need to connect +clients to use them. This guide covers two main connection scenarios: + +1. **External clients** - Connecting from outside the cluster using Ingress or + Gateway API to expose MCP servers +2. **Internal clients** - Connecting from applications running within the same + Kubernetes cluster + +```mermaid +flowchart TB + subgraph External["External clients"] + UI["ToolHive UI"] + CLI["ToolHive CLI"] + Other["Other MCP clients"] + end + + subgraph K8s["Kubernetes cluster"] + Ingress["Ingress/Gateway"] + + subgraph NS["MCP Namespace"] + ProxyService["Service: MCP proxy"] + ProxyPod["Pod: ToolHive proxy"] + MCPPod["Pod: MCP server"] + end + + subgraph AppNS["App namespace"] + App["Your application"] + end + end + + UI -->|HTTPS| Ingress + CLI -->|HTTPS| Ingress + Other -->|HTTPS| Ingress + Ingress --> ProxyService + ProxyService --> ProxyPod + ProxyPod --> MCPPod + + App -->|HTTP| ProxyService +``` + +## Prerequisites + +- A Kubernetes cluster with MCP servers deployed (see + [Run MCP servers in Kubernetes](./run-mcp-k8s.mdx)) +- An Ingress controller or Gateway API implementation installed in your cluster + (for external access) +- [`kubectl`](https://kubernetes.io/docs/tasks/tools/) configured to communicate + with your cluster + +## Connect from outside the cluster + +To make your MCP servers accessible to external clients like the ToolHive UI, +ToolHive CLI, or other MCP clients, you need to expose the proxy service using +an Ingress resource or Gateway API. + +:::info[Service naming convention] + +The ToolHive operator automatically creates a Kubernetes Service for each +MCPServer, MCPRemoteProxy, and VirtualMCPServer resource using the following +naming patterns using the resource name: + +- MCPServer: `mcp--proxy` +- MCPRemoteProxy: `mcp--remote-proxy` +- VirtualMCPServer: `vmcp-` + +For example, an MCPServer named `fetch` gets a Service named `mcp-fetch-proxy`. + +::: + +:::warning[Security requirements] + +When exposing MCP servers externally, you should: + +- Always use HTTPS with valid TLS certificates +- Configure authentication to control access (see + [Authentication and authorization](./auth-k8s.mdx)) +- Consider network policies to restrict traffic + +Running MCP servers without authentication on public networks is a security +risk. + +::: + +### Option 1: Using Ingress + +Ingress provides a stable API for exposing HTTP/HTTPS services. This example +shows a generic Ingress configuration that works with popular Ingress +controllers like Traefik, Contour, and HAProxy, as well as cloud provider +implementations like AWS Load Balancer Controller, Google Cloud Load Balancer, +and Azure Application Gateway. + +First, ensure you have an MCP server deployed. This example uses the `fetch` +server: + +```yaml title="fetch-server.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: fetch + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/gofetch/server:latest + transport: streamable-http + mcpPort: 8080 + proxyPort: 8080 +``` + +Create an Ingress resource to expose the MCP server proxy. You can use either +host-based routing (separate subdomain per server) or path-based routing (single +domain with paths): + + + + +Each MCP server gets its own subdomain: + +```yaml title="fetch-ingress.yaml" +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: fetch-mcp-ingress + namespace: toolhive-system + annotations: + cert-manager.io/cluster-issuer: 'letsencrypt-prod' +spec: + ingressClassName: traefik + tls: + - hosts: + - fetch-mcp.example.com + secretName: fetch-mcp-tls + rules: + - host: fetch-mcp.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: mcp-fetch-proxy + port: + number: 8080 +``` + +The MCP server is accessible at `https://fetch-mcp.example.com/mcp`. + + + + +Multiple MCP servers share a single domain using path prefixes. This approach +requires URL rewriting to strip the path prefix before forwarding to the backend +service. + +:::note + +Path rewriting support and syntax varies by Ingress controller. Check your +controller's documentation for the correct annotations or resources. + +::: + +```yaml title="mcp-ingress.yaml" +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: mcp-servers-ingress + namespace: toolhive-system + annotations: + cert-manager.io/cluster-issuer: 'letsencrypt-prod' + # Traefik example: strip path prefix + traefik.ingress.kubernetes.io/router.middlewares: toolhive-system-strip-mcp-prefix@kubernetescrd +spec: + ingressClassName: traefik + tls: + - hosts: + - mcp.example.com + secretName: mcp-tls + rules: + - host: mcp.example.com + http: + paths: + # Fetch MCP server + - path: /fetch + pathType: Prefix + backend: + service: + name: mcp-fetch-proxy + port: + number: 8080 + # Another MCP server + - path: / + pathType: Prefix + backend: + service: + name: mcp--proxy + port: + number: 8080 +--- +# Traefik Middleware to strip path prefixes +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: strip-mcp-prefix + namespace: toolhive-system +spec: + stripPrefix: + prefixes: + - /fetch + - / +``` + +The MCP servers are accessible at `https://mcp.example.com/fetch/mcp` and +`https://mcp.example.com//mcp`. + + + + +This example is the same as the previous path-based routing example but includes +additional rules in the Ingress to direct the `.well-known` path for each MCP +server to the corresponding backend. This is necessary when using OAuth +authentication since the OAuth flow requires access to the `.well-known` +endpoint for discovery. + +First, in the MCPServer spec for each server, ensure the `resourceUrl` property +is set to the full client-facing URL: + +```yaml title="fetch-server-oauth.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +# ... +spec: + oidcConfig: + type: inline + resourceUrl: https://mcp.example.com//mcp + inline: + # ... other OIDC config ... +``` + +The `inline.audience` value should match the audience expected by your identity +provider, and is likely the same for all servers using the same authorization +server. See [Authentication and authorization](./auth-k8s.mdx) for full OIDC +setup instructions. + +Configure the Ingress with additional rules for the `.well-known` paths of each +MCP server that has OAuth enabled: + +:::note + +Path rewriting support and syntax varies by Ingress controller. Check your +controller's documentation for the correct annotations or resources. + +::: + +```yaml {28-34,43-49} title="mcp-ingress.yaml" +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: mcp-servers-ingress + namespace: toolhive-system + annotations: + cert-manager.io/cluster-issuer: 'letsencrypt-prod' + # Traefik example: strip path prefix + traefik.ingress.kubernetes.io/router.middlewares: toolhive-system-strip-mcp-prefix@kubernetescrd +spec: + ingressClassName: traefik + tls: + - hosts: + - mcp.example.com + secretName: mcp-tls + rules: + - host: mcp.example.com + http: + paths: + # Fetch MCP server + - path: /fetch + pathType: Prefix + backend: + service: + name: mcp-fetch-proxy + port: + number: 8080 + - path: /.well-known/oauth-protected-resource/fetch/mcp + pathType: Exact + backend: + service: + name: mcp-fetch-proxy + port: + number: 8080 + # Another MCP server + - path: / + pathType: Prefix + backend: + service: + name: mcp--proxy + port: + number: 8080 + - path: /.well-known/oauth-protected-resource//mcp + pathType: Exact + backend: + service: + name: mcp--proxy + port: + number: 8080 +--- +# Traefik Middleware to strip path prefixes +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: strip-mcp-prefix + namespace: toolhive-system +spec: + stripPrefix: + prefixes: + - /fetch + - / +``` + +The MCP servers are accessible at `https://mcp.example.com/fetch/mcp` and +`https://mcp.example.com//mcp`. + + + + +Apply the resources: + +```bash +kubectl apply -f fetch-server.yaml +kubectl apply -f fetch-ingress.yaml # or mcp-ingress.yaml for path-based +``` + +Verify the Ingress is configured: + +```bash +kubectl get ingress -n toolhive-system +``` + +### Option 2: Using Gateway API + +The [Gateway API](https://gateway-api.sigs.k8s.io/) is a more expressive way to +expose services and is the successor to Ingress. This example works with Gateway +API implementations like Cilium, Istio, Envoy Gateway, and Traefik, as well as +cloud provider implementations like AWS Gateway API Controller, Google +Kubernetes Engine (GKE) Gateway controller, and Azure Application Gateway for +Containers. See the +[full list of implementations](https://gateway-api.sigs.k8s.io/implementations/). + +:::tip + +For a complete working example using the ngrok Gateway API implementation, see +the +[Configure secure ingress for MCP servers on Kubernetes](../integrations/ingress-ngrok.mdx) +tutorial. + +::: + +Many Gateway API implementations create a Gateway resource automatically during +installation. For example, Traefik's Helm chart creates a `traefik-gateway` in +the default namespace when enabled. Check if a Gateway already exists: + +```bash +kubectl get gateway --all-namespaces +``` + +If a Gateway exists, note its name and namespace to use in your HTTPRoute. If +you need to create a new Gateway, use this example: + +```yaml title="mcp-gateway.yaml" +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: mcp-gateway + namespace: toolhive-system +spec: + gatewayClassName: traefik # Change to match your Gateway implementation + listeners: + - name: https + protocol: HTTPS + port: 443 + tls: + mode: Terminate + certificateRefs: + - name: mcp-gateway-cert + allowedRoutes: + namespaces: + from: Same +``` + +Create an HTTPRoute to expose your MCP server. You can use either host-based +routing (separate subdomain per server) or path-based routing (single domain +with paths): + + + + +Each MCP server gets its own subdomain: + +```yaml title="fetch-route.yaml" +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: fetch-mcp-route + namespace: toolhive-system +spec: + parentRefs: + - name: mcp-gateway # Reference your Gateway name (e.g., traefik-gateway) + # namespace: default # Uncomment if Gateway is in a different namespace + hostnames: + - fetch-mcp.example.com # Change to your domain + rules: + - backendRefs: + - name: mcp-fetch-proxy # Format: mcp--proxy + port: 8080 # This matches the proxyPort +``` + +The MCP server is accessible at `https://fetch-mcp.example.com/mcp`. + + + + +Multiple MCP servers share a single domain using path prefixes. This approach +uses URL rewriting to strip the path prefix before forwarding to the backend +service. + +```yaml title="mcp-routes.yaml" +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: mcp-servers-route + namespace: toolhive-system +spec: + parentRefs: + - name: mcp-gateway # Reference your Gateway name (e.g., traefik-gateway) + # namespace: default # Uncomment if Gateway is in a different namespace + hostnames: + - mcp.example.com # Change to your domain + rules: + # Fetch MCP server + - matches: + - path: + type: PathPrefix + value: /fetch + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplacePrefixMatch + replacePrefixMatch: / + backendRefs: + - name: mcp-fetch-proxy # Format: mcp--proxy + port: 8080 # This matches the proxyPort + # Another MCP server + - matches: + - path: + type: PathPrefix + value: / + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplacePrefixMatch + replacePrefixMatch: / + backendRefs: + - name: mcp--proxy + port: 8080 +``` + +The MCP servers are accessible at `https://mcp.example.com/fetch/mcp` and +`https://mcp.example.com//mcp`. + +The `URLRewrite` filter removes the path prefix (e.g., `/fetch`) before +forwarding requests to the backend service, so the MCP server receives requests +at `/mcp` as expected. + + + + +This example is the same as the previous path-based routing example but includes +additional rules in the HTTPRoute to direct the `.well-known` path for each MCP +server to the corresponding backend. This is necessary when using OAuth +authentication since the OAuth flow requires access to the `.well-known` +endpoint for discovery. + +First, in the MCPServer spec for each server, ensure the `resourceUrl` property +is set to the full client-facing URL: + +```yaml title="fetch-server-oauth.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +# ... +spec: + oidcConfig: + type: inline + resourceUrl: https://mcp.example.com//mcp + inline: + # ... other OIDC config ... +``` + +The `inline.audience` value should match the audience expected by your identity +provider, and is likely the same for all servers using the same authorization +server. See [Authentication and authorization](./auth-k8s.mdx) for full OIDC +setup instructions. + +Configure the HTTPRoute with additional rules for the `.well-known` paths of +each MCP server that has OAuth enabled: + +```yaml {27-33,48-54} title="mcp-routes.yaml" +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: mcp-servers-route + namespace: toolhive-system +spec: + parentRefs: + - name: mcp-gateway # Reference your Gateway name (e.g., traefik-gateway) + # namespace: default # Uncomment if Gateway is in a different namespace + hostnames: + - mcp.example.com # Change to your domain + rules: + # Fetch MCP server + - matches: + - path: + type: PathPrefix + value: /fetch + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplacePrefixMatch + replacePrefixMatch: / + backendRefs: + - name: mcp-fetch-proxy # Format: mcp--proxy + port: 8080 # This matches the proxyPort + - matches: + - path: + type: Exact + value: /.well-known/oauth-protected-resource/fetch/mcp + backendRefs: + - name: mcp-fetch-proxy + port: 8080 + # Another MCP server + - matches: + - path: + type: PathPrefix + value: / + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplacePrefixMatch + replacePrefixMatch: / + backendRefs: + - name: mcp--proxy + port: 8080 + - matches: + - path: + type: Exact + value: /.well-known/oauth-protected-resource//mcp + backendRefs: + - name: mcp--proxy + port: 8080 +``` + +The MCP servers are accessible at `https://mcp.example.com/fetch/mcp` and +`https://mcp.example.com//mcp`. + +The `URLRewrite` filter removes the path prefix (e.g., `/fetch`) before +forwarding requests to the backend service, so the MCP server receives requests +at `/mcp` as expected. + + + + +Apply the resources: + +```bash +kubectl apply -f mcp-gateway.yaml # If creating a new Gateway +kubectl apply -f fetch-route.yaml # or mcp-routes.yaml for path-based +``` + +Verify the route is configured: + +```bash +kubectl get httproute -n toolhive-system +``` + +### TLS certificates + +For production deployments, use valid TLS certificates from a trusted +certificate authority. The most common approaches are: + + + + +[cert-manager](https://cert-manager.io/) automates certificate management in +Kubernetes. Install cert-manager and create a ClusterIssuer: + +```yaml title="letsencrypt-issuer.yaml" +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: letsencrypt-prod +spec: + acme: + server: https://acme-v02.api.letsencrypt.org/directory + email: your-email@example.com # Change to your email + privateKeySecretRef: + name: letsencrypt-prod + solvers: + - http01: + ingress: + class: traefik # Change to match your Ingress controller +``` + +Apply it: + +```bash +kubectl apply -f letsencrypt-issuer.yaml +``` + +The Ingress example above already includes the cert-manager annotation. Once +cert-manager is installed, it will automatically provision and renew +certificates. + + + + +If you have existing certificates, create a Kubernetes Secret: + +```bash +kubectl create secret tls fetch-mcp-tls \ + --cert=path/to/tls.crt \ + --key=path/to/tls.key \ + -n toolhive-system +``` + +Reference this secret in your Ingress or Gateway configuration as shown in the +examples above. + + + + +### Connect with ToolHive UI or CLI + +Once your MCP server is exposed with HTTPS, you can connect to it as a remote +MCP server from the ToolHive UI or CLI. + + + + +In the ToolHive UI: + +1. Click **Add an MCP server** on the **MCP Servers** page +2. Select **Add a remote MCP server** +3. Enter the connection details: + - **Name**: A friendly name for the server + - **Server URL**: Use the appropriate URL based on your routing approach: + - Host-based: `https://fetch-mcp.example.com/mcp` + - Path-based: `https://mcp.example.com/fetch/mcp` + - **Transport**: Streamable HTTP (or SSE if your server uses SSE) +4. If authentication is configured, select the method and enter the required + OAuth or OIDC details +5. Click **Install server** + +The MCP server appears in your server list and you can use it with any connected +MCP client. + + + + +Use the `thv run` command to connect: + +```bash +# Host-based routing: separate subdomain per server +thv run --name fetch-k8s https://fetch-mcp.example.com/mcp + +# Path-based routing: single domain with paths +thv run --name fetch-k8s https://mcp.example.com/fetch/mcp +``` + +If authentication is configured, add the appropriate flags. See the +[ToolHive CLI guide](../guides-cli/run-mcp-servers.mdx#authentication-setup) for +details. + +The MCP server is now available to your configured MCP clients. + + + + +For more details on using remote MCP servers, see: + +- [Run remote MCP servers (UI)](../guides-ui/run-mcp-servers.mdx?custom-type=custom_remote#install-a-custom-mcp-server) +- [Run remote MCP servers (CLI)](../guides-cli/run-mcp-servers.mdx#run-a-remote-mcp-server) + +## Connect from within the cluster + +Applications running inside your Kubernetes cluster can connect directly to MCP +server proxy services using Kubernetes service discovery. This is more efficient +and secure than routing through an external Ingress. + +### Service DNS names + +Each MCPServer, MCPRemoteProxy, or VirtualMCPServer automatically gets a +Kubernetes Service that other pods can use to connect using the following naming +patterns: + +- MCPServer: `mcp--proxy` +- MCPRemoteProxy: `mcp--remote-proxy` +- VirtualMCPServer: `vmcp-` + +The full DNS name follows the standard Kubernetes pattern: + +```text +..svc.cluster.local: +``` + +For example, if you have an MCPServer named `fetch` in the `toolhive-system` +namespace with `proxyPort: 8080`, the full URL is: + +```text +http://mcp-fetch-proxy.toolhive-system.svc.cluster.local:8080 +``` + +Within the same namespace, you can use the short form: + +```text +http://mcp-fetch-proxy:8080 +``` + +### Example: Configuring applications + +When deploying applications like AI agents in the same cluster, configure them +to use the service DNS name. This example shows how to pass the connection URL +as an environment variable: + +```yaml title="agent-app.yaml" +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-agent-app + namespace: my-app +spec: + # ... other deployment configuration ... + template: + spec: + containers: + - name: app + image: my-agent-app:latest + env: + - name: MCP_SERVER_URL + # Different namespace: use full DNS name + value: 'http://mcp-fetch-proxy.toolhive-system.svc.cluster.local:8080/mcp' + # Same namespace: use short name + # value: 'http://mcp-fetch-proxy:8080/mcp' +``` + +### Network policies for cross-namespace access + +If your cluster uses network policies, you may need to create a policy to allow +traffic between namespaces: + +```yaml title="allow-cross-namespace.yaml" +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-app-to-mcp + namespace: toolhive-system +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: toolhive-proxy + policyTypes: + - Ingress + ingress: + - from: + - namespaceSelector: + matchLabels: + name: my-app # Your app's namespace must have this label +``` + +## Authentication for external clients + +When exposing MCP servers externally, configure authentication to control +access. ToolHive supports multiple authentication methods: + +- **OIDC authentication** - Use external identity providers like Google, GitHub, + Okta, or Microsoft Entra ID +- **Kubernetes service accounts** - For service-to-service authentication within + the cluster + +See the [Authentication and authorization](./auth-k8s.mdx) guide for detailed +setup instructions. + +## Check connection status + +### Test external connectivity + +If you have the ToolHive CLI installed, you can test connectivity to your MCP +server: + +```bash +thv mcp list tools --server https://fetch-mcp.example.com/mcp +``` + +Or use `curl` to send a JSON-RPC request: + +```bash +# Host-based routing +curl -X POST https://fetch-mcp.example.com/mcp \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' + +# Path-based routing +curl -X POST https://mcp.example.com/fetch/mcp \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' +``` + +You should receive a JSON response with a list of available tools. + +:::tip + +If you've configured authentication on your MCP server, see the +[Authentication and authorization](./auth-k8s.mdx) guide for how to test +authenticated connections. + +::: + +### Test internal connectivity + +Test connectivity from within the cluster: + +```bash +# Port-forward to test locally +kubectl port-forward -n toolhive-system service/mcp-fetch-proxy 8080:8080 + +# In another terminal, test the connection +curl -X POST http://localhost:8080/mcp \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' +``` + +### Verify the connection path + +Check that the Ingress or Gateway is properly configured and the Service has +running pods: + +```bash +# For Ingress: verify it exists and has an address +kubectl get ingress -n toolhive-system fetch-mcp-ingress + +# For Gateway API: check HTTPRoute status (look for "Accepted: True" in conditions) +kubectl describe httproute -n toolhive-system fetch-mcp-route + +# Verify the Service exists +kubectl get service -n toolhive-system mcp-fetch-proxy + +# Check that the proxy pod is running +kubectl get pods -n toolhive-system -l app.kubernetes.io/instance=fetch +``` + +If the Ingress shows an address or the HTTPRoute status shows "Accepted: True", +and the pod is running, the connection path is properly configured. + +## Next steps + +Learn how to secure your MCP servers with +[Authentication and authorization](./auth-k8s.mdx). + +Configure [Telemetry and metrics](./telemetry-and-metrics.mdx) to monitor your +MCP server usage and performance. + +[Set up logging](./logging.mdx) to track requests and audit MCP server activity. + +## Related information + +- [Run MCP servers in Kubernetes](./run-mcp-k8s.mdx) - Deploy MCP servers in + your cluster +- [Proxy remote MCP servers](./remote-mcp-proxy.mdx) - Create proxies for + external MCP servers +- [Client compatibility](../reference/client-compatibility.mdx) - Supported MCP + clients and configuration +- [Kubernetes CRD reference](../reference/crd-spec.md#apiv1alpha1mcpserver) - + Full MCPServer specification +- [Configure secure ingress tutorial](../integrations/ingress-ngrok.mdx) - + Complete tutorial using ngrok and Gateway API + +## Troubleshooting + +
+Ingress returns 503 Service Unavailable + +If your Ingress returns a 503 error: + +```bash +# Check if the service exists +kubectl get service -n toolhive-system mcp-fetch-proxy + +# Check the proxy pod is running +kubectl get pods -n toolhive-system -l app.kubernetes.io/instance=fetch + +# Check pod logs for errors +kubectl logs -n toolhive-system -l app.kubernetes.io/instance=fetch +``` + +Common causes: + +- **Proxy pod not running**: Ensure the MCPServer resource was created + successfully +- **Wrong service name**: The Ingress backend service name must follow the + naming conventions defined above +- **Wrong port**: The Ingress backend port must match the `proxyPort` in the + MCPServer spec +- **Pod health check failing**: Check the proxy pod logs for errors + +
+ +
+TLS certificate issues + +If you see certificate errors when connecting: + +```bash +# Check the certificate secret exists +kubectl get secret -n toolhive-system fetch-mcp-tls + +# Describe the secret to verify it contains tls.crt and tls.key +kubectl describe secret -n toolhive-system fetch-mcp-tls + +# If using cert-manager, check certificate status +kubectl get certificate -n toolhive-system + +# Check cert-manager logs +kubectl logs -n cert-manager -l app=cert-manager +``` + +Common causes: + +- **Certificate not ready**: Wait for cert-manager to provision the certificate + (can take a few minutes) +- **DNS not configured**: Ensure your domain points to the Ingress load balancer +- **Challenge validation failing**: Check cert-manager logs for ACME challenge + errors +- **Wrong ClusterIssuer**: Verify the cert-manager annotation references an + existing ClusterIssuer + +
+ +
+Authentication failures + +If OAuth authentication is failing when connecting to your MCP server: + +```bash +# Test if the OAuth metadata endpoint is accessible +# For host-based routing +curl https://fetch-mcp.example.com/.well-known/oauth-protected-resource + +# For path-based routing +curl https://mcp.example.com/.well-known/oauth-protected-resource/fetch/mcp + +# Check proxy logs for authentication errors +kubectl logs -n toolhive-system -l app.kubernetes.io/instance=fetch + +# Verify the MCPServer OIDC configuration +kubectl get mcpserver -n toolhive-system fetch -o yaml | grep -A15 oidcConfig +``` + +Common causes: + +- **`.well-known` endpoint not accessible**: When using path-based routing with + OAuth, you must configure your Ingress or HTTPRoute to forward the + `.well-known` path to the backend. See the "Path-based routing with OAuth" tab + in the Ingress or Gateway API sections above for configuration examples. +- **Missing or incorrect `resourceUrl`**: Ensure the `resourceUrl` in your + MCPServer's `oidcConfig` matches the client-facing URL (e.g., + `https://mcp.example.com/fetch/mcp` for path-based routing). +- **Token validation failure**: The token's `aud` claim must match the + configured audience in your MCPServer's OIDC configuration. +- **Issuer mismatch**: The token's `iss` claim must match the configured issuer. +- **JWKS endpoint unreachable**: Check that the proxy can reach your identity + provider's JWKS endpoint to validate tokens. + +For detailed OAuth setup and troubleshooting, see the +[Authentication and authorization](./auth-k8s.mdx) guide. + +
+ +
+Cannot connect from within cluster + +If pods cannot connect to the MCP server service: + +```bash +# Verify service exists +kubectl get service -n toolhive-system mcp-fetch-proxy + +# Check that pods are running +kubectl get pods -n toolhive-system -l app.kubernetes.io/instance=fetch + +# Test DNS resolution from a pod +kubectl run test-dns -n toolhive-system --image=busybox --restart=Never -- \ + nslookup mcp-fetch-proxy.toolhive-system.svc.cluster.local + +# Check the DNS test results +kubectl logs -n toolhive-system test-dns + +# Clean up the test pod +kubectl delete pod -n toolhive-system test-dns + +# Test connectivity from a pod +kubectl run test-curl -n toolhive-system --image=curlimages/curl --restart=Never -- \ + curl -X POST http://mcp-fetch-proxy:8080/mcp \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' + +# Check the connectivity test results +kubectl logs -n toolhive-system test-curl + +# Clean up the test pod +kubectl delete pod -n toolhive-system test-curl +``` + +Common causes: + +- **Network policies blocking traffic**: Check for network policies that might + prevent pod-to-pod communication +- **Wrong namespace**: Ensure you're using the correct service DNS name for + cross-namespace access +- **Service not created**: The operator automatically creates services, but + verify it exists +- **Wrong port**: Ensure you're using the `proxyPort` value from the MCPServer + spec +- **Wrong service name**: Remember the service name follows the naming + conventions defined above + +
+ +
+Gateway API not working + +If using Gateway API and connections fail: + +```bash +# Check Gateway status +kubectl get gateway -n toolhive-system mcp-gateway + +# Check HTTPRoute status +kubectl get httproute -n toolhive-system fetch-mcp-route + +# Describe the HTTPRoute for detailed status +kubectl describe httproute -n toolhive-system fetch-mcp-route + +# Check Gateway implementation logs (example for Istio) +kubectl logs -n istio-system -l app=istio-ingressgateway +``` + +Common causes: + +- **Gateway not ready**: Wait for the Gateway to be accepted and programmed by + the controller +- **Wrong gateway class**: Ensure `gatewayClassName` matches your installed + Gateway API implementation +- **Listener configuration issues**: Verify the Gateway listener configuration + matches the HTTPRoute requirements +- **Certificate issues**: For HTTPS, ensure the certificate reference exists and + is valid + +
+ +
+Cross-namespace access denied + +If cross-namespace connections fail: + +```bash +# Check network policies in the MCP server namespace +kubectl get networkpolicy -n toolhive-system + +# Describe network policies to see rules +kubectl describe networkpolicy -n toolhive-system + +# Check if the app namespace has the required labels +kubectl get namespace my-app --show-labels +``` + +Solutions: + +- Create or update network policies to allow traffic from your app namespace +- Add required labels to your application namespace +- Test connectivity using a debug pod in the app namespace + +
diff --git a/versioned_docs/version-1.0/toolhive/guides-k8s/customize-tools.mdx b/versioned_docs/version-1.0/toolhive/guides-k8s/customize-tools.mdx new file mode 100644 index 00000000..3d2453a4 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-k8s/customize-tools.mdx @@ -0,0 +1,241 @@ +--- +title: Customize tools +description: Filter and rename MCP server tools using the MCPToolConfig CRD and + toolConfigRef. +--- + +## Overview + +Use the MCPToolConfig Custom Resource Definition (CRD) to centrally manage which +tools an MCP server exposes, and optionally rename tools or override their +descriptions. You reference the configuration from an MCPServer using the +`toolConfigRef` field. + +- toolsFilter: allow-list the tools to expose. +- toolsOverride: rename tools and/or change their descriptions. +- Same-namespace only: an MCPServer can reference only MCPToolConfig objects in + the same namespace. +- Precedence: toolConfigRef takes precedence over the deprecated spec.tools + field on MCPServer. + +## Define a basic tool filter + +This example exposes only three tools on a server: + +```yaml title="toolconfig-basic.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPToolConfig +metadata: + name: basic-tool-filter + namespace: toolhive-system +spec: + toolsFilter: + - read_file + - write_file + - list_directory +``` + +:::note[Empty filter] + +If `toolsFilter` is omitted or empty, all tools are allowed. + +::: + +## Rename tools and override descriptions + +You can rename tools to clearly distinguish different deployments or scopes, and +refine their descriptions to add usage guidance. + +A common pattern is running the same MCP server multiple times for different +scopes (for example, separate GitHub orgs, repos, or environments). Renaming +tools makes intent obvious and helps prevent mistakes. + +```yaml title="toolconfig-with-overrides.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPToolConfig +metadata: + name: github-tools-config + namespace: toolhive-system +spec: + # Only expose GitHub PR-related tools + toolsFilter: + - create_pull_request + - get_pull_request + - list_pull_requests + - merge_pull_request + + # You can override name, description, or both (they are independent) + toolsOverride: + # Override only the name + create_pull_request: + name: github_create_pr + + # Override only the description (keep the original name) + get_pull_request: + description: Retrieve details of a specific GitHub pull request + + # Override both name and description + list_pull_requests: + name: github_list_prs + description: List pull requests in a repository + + merge_pull_request: + name: github_merge_pr + description: Merge a GitHub pull request +``` + +The key in the `toolsOverride` map is the original tool name; the `name` field +is the user-visible (overridden) name. + +:::info[Override fields] + +You can override name or description independently. Leave one unset to keep the +original value from the MCP server. Both fields cannot be empty at the same +time. + +::: + +:::tip[When to rename?] + +If you run the same server for different scopes (for example, prod vs. sandbox), +use distinct tool names like `github_prod_create_pr` and +`github_sandbox_create_pr` to make intent clear to clients. + +::: + +## Reference the configuration from an MCP server + +Add `toolConfigRef` to the `spec` section of your MCPServer or MCPRemoteProxy +resource. + + + + +```yaml {10-11} title="mcpserver-with-toolconfig.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: github + namespace: toolhive-system +spec: + image: ghcr.io/github/github-mcp-server + transport: stdio + proxyPort: 8080 + toolConfigRef: + name: github-tools-config + # ... other spec fields ... +``` + + + + +```yaml {10-11} title="mcpremoteproxy-with-toolconfig.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRemoteProxy +metadata: + name: github + namespace: toolhive-system +spec: + remoteUrl: https://github.com/github/github-mcp-server + transport: streamable-http + proxyPort: 8080 + toolConfigRef: + name: github-tools-config + # ... auth config ... +``` + + + + +:::note[Filtering and overrides together] + +When you use `toolsFilter` and `toolsOverride` together, filter by the +user-visible (overridden) names. Tool calls using overridden names are forwarded +to the actual tool. + +::: + +## Example: org-scoped tool names + +Run the GitHub MCP twice, once per organization, and rename tools so intent is +clear to clients. + +```yaml title="github-org-scoped-tools.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPToolConfig +metadata: + name: github-acme-tools + namespace: toolhive-system +spec: + toolsFilter: + - github_acme_create_pr + - github_acme_get_pr + toolsOverride: + create_pull_request: + name: github_acme_create_pr + get_pull_request: + name: github_acme_get_pr +--- +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPToolConfig +metadata: + name: github-foocorp-tools + namespace: toolhive-system +spec: + toolsFilter: + - github_foocorp_create_pr + - github_foocorp_get_pr + toolsOverride: + create_pull_request: + name: github_foocorp_create_pr + get_pull_request: + name: github_foocorp_get_pr +--- +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: github-acme + namespace: toolhive-system +spec: + image: ghcr.io/github/github-mcp-server + transport: stdio + proxyPort: 8080 + # (Use credentials that scope access to the acme-org here) + toolConfigRef: + name: github-acme-tools +--- +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: github-foocorp + namespace: toolhive-system +spec: + image: ghcr.io/github/github-mcp-server + transport: stdio + proxyPort: 8080 + # (Use credentials that scope access to the foo-corp org here) + toolConfigRef: + name: github-foocorp-tools +``` + +## Inspect status and troubleshoot + +Use kubectl to inspect status and track change propagation: + +```bash +kubectl -n toolhive-system get mcptoolconfigs +kubectl -n toolhive-system get mcptoolconfig github-tools-config -o yaml +kubectl -n toolhive-system get mcpserver github -o yaml +``` + +- If an MCPToolConfig is still referenced, deletion is blocked by a finalizer. + Remove all toolConfigRef references first. +- If an MCPServer references a missing MCPToolConfig, the server enters Failed + and the controller logs include the missing name and namespace. + +## Related information + +- See the + [Kubernetes CRD reference](../reference/crd-spec.md#apiv1alpha1mcptoolconfig) + for the full MCPToolConfig and MCPServerSpec schemas. +- Learn how to [run the MKP server in Kubernetes](../guides-mcp/k8s.mdx). diff --git a/versioned_docs/version-1.0/toolhive/guides-k8s/deploy-operator.mdx b/versioned_docs/version-1.0/toolhive/guides-k8s/deploy-operator.mdx new file mode 100644 index 00000000..7f586d00 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-k8s/deploy-operator.mdx @@ -0,0 +1,596 @@ +--- +title: Deploy the operator +description: + How to deploy the ToolHive operator in a Kubernetes cluster using Helm or + kubectl +--- + +## Prerequisites + +- A Kubernetes cluster (current and two previous minor versions are supported) +- Permissions to create resources in the cluster +- [`kubectl`](https://kubernetes.io/docs/tasks/tools/) configured to communicate + with your cluster +- [Helm](https://helm.sh/docs/intro/install/) (v3.10 minimum, v3.14+ + recommended) + +## Install the CRDs + +![Latest CRD Helm chart release](https://img.shields.io/github/v/release/stacklok/toolhive?style=for-the-badge&logo=helm&label=Latest%20CRD%20chart&color=bddfc2) + +The ToolHive operator requires Custom Resource Definitions (CRDs) to manage +MCPServer resources. The CRDs define the structure and behavior of MCPServers in +your cluster. + +Choose an installation method based on your needs: + +- **Helm** (recommended): Provides customization options and manages the full + lifecycle of the operator. CRDs are installed and upgraded automatically as + part of the Helm chart. +- **kubectl**: Uses static manifests for a simple installation. Useful for + environments where Helm isn't available or for GitOps workflows. + + + + +This command installs the latest version of the ToolHive operator CRDs Helm +chart: + +```bash +helm upgrade --install toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds \ + -n toolhive-system --create-namespace +``` + +:::warning[Namespace consistency] + +When you install this chart, Helm stamps all CRDs with a +`meta.helm.sh/release-namespace` annotation set to the namespace used at install +time and is fixed for that release. You must continue to use the same namespace +on all future `helm upgrade` commands for the CRDs. If you decide to specify a +different namespace, an error will occur due to ownership issues. + +If you need to migrate to a different namespace, see the +[CRD namespace mismatch troubleshooting](#crd-upgrade-fails-with-namespace-mismatch) +section. + +::: + +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 \ + -n toolhive-system --version 0.12.1 +``` + +#### CRD configuration options + +The Helm chart provides fine-grained control over which CRDs are installed. By +default, all CRDs are installed. You can selectively enable or disable CRD +groups using these values: + +| Value | Description | Default | +| ------------------------- | ----------------------------------------------------- | ------- | +| `crds.install.server` | Install server CRDs (MCPServer, MCPRemoteProxy, etc.) | `true` | +| `crds.install.registry` | Install registry CRDs (MCPRegistry) | `true` | +| `crds.install.virtualMcp` | Install vMCP CRDs (VirtualMCPServer, etc.) | `true` | +| `crds.keep` | Preserve CRDs when uninstalling the chart | `true` | + +For example, to install only server-related CRDs without vMCP support: + +```bash +helm upgrade --install toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds \ + -n toolhive-system --set crds.install.virtualMcp=false +``` + +:::note + +The `crds.keep` option adds the `helm.sh/resource-policy: keep` annotation to +CRDs, which prevents Helm from deleting them during `helm uninstall`. This +protects your custom resources from accidental deletion. If you want to remove +CRDs during uninstall, set `crds.keep=false`. + +::: + + + + +To install the CRDs using `kubectl`, run the following, ensuring you only apply +the CRDs you need for the features you plan to use: + +```bash +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_embeddingservers.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcptoolconfigs.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpremoteproxies.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpservers.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpgroups.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpregistries.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_virtualmcpservers.yaml +``` + +Replace `v0.12.1` in the commands above with your target CRD version. + + + + +## Install the operator + +![Latest Operator Helm chart release](https://img.shields.io/github/v/release/stacklok/toolhive?style=for-the-badge&logo=helm&label=Latest%20Operator%20chart&color=bddfc2) + +To install the ToolHive operator using default settings, run the following +command: + +```bash +helm upgrade --install toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator -n toolhive-system --create-namespace +``` + +This command installs the latest version of the ToolHive operator CRDs Helm +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.12.1 +``` + +Verify the installation: + +```bash +kubectl get pods -n toolhive-system +``` + +After about 30 seconds, you should see the `toolhive-operator` pod running. + +Check the logs of the operator pod: + +```bash +kubectl logs -f -n toolhive-system +``` + +This shows you the logs of the operator pod, which can help you debug any +issues. For comprehensive logging and audit capabilities, see the +[Logging infrastructure](./logging.mdx) guide. + +## Customize the operator + +You can customize the operator installation by providing a `values.yaml` file +with your configuration settings. For example, to change the number of replicas +and set a specific ToolHive version, create a `values.yaml` file: + +```yaml title="values.yaml" +operator: + replicaCount: 2 + toolhiveRunnerImage: ghcr.io/stacklok/toolhive:v0.2.17 # or `latest` +``` + +Install the operator with your custom values: + +```bash {3} +helm upgrade --install toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator\ + -n toolhive-system --create-namespace\ + -f values.yaml +``` + +To see all available configuration options, run: + +```bash +helm show values oci://ghcr.io/stacklok/toolhive/toolhive-operator +``` + +## Operator deployment modes + +The ToolHive operator supports two distinct deployment modes to accommodate +different security requirements and organizational structures. + +### Cluster mode (default) + +Cluster mode provides the operator with cluster-wide access to manage MCPServer +resources in any namespace. This is the default mode and is suitable for +platform teams managing MCPServers across the entire cluster. + +**Characteristics:** + +- Full cluster-wide access to manage MCPServers in any namespace +- Uses `ClusterRole` and `ClusterRoleBinding` for broad permissions +- Simplest configuration and management +- Best for single-tenant clusters or trusted environments + +To explicitly configure cluster mode, include the following property in your +Helm `values.yaml` file: + +```yaml title="values.yaml" +operator: + rbac: + scope: 'cluster' +``` + +Reference the `values.yaml` file when you install the operator using Helm: + +```bash {3} +helm upgrade --install toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \ + -n toolhive-system --create-namespace + -f values.yaml +``` + +This is the default configuration used in the standard installation commands. + +### Namespace mode + +Namespace mode restricts the operator's access to only specified namespaces. +This mode is perfect for multi-tenant environments and organizations following +the principle of least privilege. + +**Characteristics:** + +- Restricted access to only specified namespaces +- Uses `ClusterRole` with namespace-specific `RoleBindings` for precise access + control +- Enhanced security through reduced blast radius +- Ideal for multi-tenant environments and compliance requirements + +To configure namespace mode, include the following in your Helm `values.yaml`: + +```yaml title="values.yaml" +operator: + rbac: + scope: 'namespace' + allowedNamespaces: + - 'team-frontend' + - 'team-backend' + - 'staging' + - 'production' +``` + +This example lets the operator manage MCPServer resources in the four namespaces +listed in the `allowedNamespaces` property. Adjust the list to match your +environment. + +Reference the `values.yaml` file when you install the operator using Helm: + +```bash {3} +helm upgrade --install toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \ + -n toolhive-system --create-namespace + -f values.yaml +``` + +Verify the RoleBindings are created: + +```bash +kubectl get rolebinding --all-namespaces | grep toolhive +``` + +You should see RoleBindings in the specified namespaces, granting the operator +access to manage MCPServers. Example output: + +```text +NAMESPACE NAME ROLE +team-frontend toolhive-operator-manager-rolebinding ClusterRole/toolhive-operator-manager-role +team-backend toolhive-operator-manager-rolebinding ClusterRole/toolhive-operator-manager-role +staging toolhive-operator-manager-rolebinding ClusterRole/toolhive-operator-manager-role +production toolhive-operator-manager-rolebinding ClusterRole/toolhive-operator-manager-role +toolhive-system toolhive-operator-leader-election-rolebinding Role/toolhive-operator-leader-election-role +``` + +### Migrate between modes + +You can switch between cluster mode and namespace mode by updating the +`values.yaml` file and reapplying the Helm chart as shown above. Migration in +both directions is supported. + +## Check operator status + +To verify the operator is working correctly: + +```bash +# Verify CRDs are installed +kubectl get crd | grep toolhive + +# Check operator deployment status +kubectl get deployment -n toolhive-system toolhive-operator + +# Check operator service account and RBAC +kubectl get serviceaccount -n toolhive-system +kubectl get clusterrole | grep toolhive +kubectl get clusterrolebinding | grep toolhive + +# Check operator pod status +kubectl get pods -n toolhive-system +# Check operator pod logs +kubectl logs -n toolhive-system +``` + +## Upgrade the operator + +To upgrade the ToolHive operator to a new version, you need to upgrade both the +CRDs and the operator installation. + +### Upgrade the CRDs + +Choose an upgrade method based on your needs: + +- **Helm** (recommended): Provides customization options and manages the full + lifecycle of the operator. CRDs are installed and upgraded automatically as + part of the Helm chart. +- **kubectl**: Uses static manifests for a simple installation. Useful for + environments where Helm isn't available or for GitOps workflows. + +![Latest CRD Helm chart release](https://img.shields.io/github/v/release/stacklok/toolhive?style=for-the-badge&logo=helm&label=Latest%20CRD%20chart&color=bddfc2) + + + + +To upgrade the ToolHive operator to a new version, upgrade the CRDs first by +upgrading with the desired CRDs chart: + +```bash +helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds --version 0.12.1 +``` + + + + +To upgrade the CRDs using `kubectl`, run the following, ensuring you only apply +the CRDs you need for the features you want: + +```bash +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_embeddingservers.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcptoolconfigs.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpremoteproxies.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpservers.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpgroups.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpregistries.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_virtualmcpservers.yaml +``` + + + + +Replace `v0.12.1` in the commands above with your target CRD version. + +### Upgrade the operator Helm release + +Then, upgrade the operator installation using Helm. + +![Latest Operator Helm chart release](https://img.shields.io/github/v/release/stacklok/toolhive?style=for-the-badge&logo=helm&label=Latest%20Operator%20chart&color=bddfc2) + +```bash +helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator -n toolhive-system +``` + +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 --version 0.12.1 +``` + +If you have a custom `values.yaml` file, include it with the `-f` flag: + +```bash +helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator -n toolhive-system -f values.yaml +``` + +## Uninstall the operator + +To uninstall the operator and CRDs: + +First, uninstall the operator: + +```bash +helm uninstall toolhive-operator -n toolhive-system +``` + +Then, if you want to completely remove ToolHive including all CRDs and related +resources, delete the CRDs. + +:::warning + +This will delete all MCPServer and related resources in your cluster! + +::: + + + + +```bash +helm uninstall toolhive-operator-crds +``` + +:::note + +If you installed the CRDs with Helm and have `crds.keep` still set to `true`, +first upgrade the chart with `--set crds.keep=false` so that when you uninstall +the CRDs chart, it completely removes all CRDs too: + +```bash +helm upgrade toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds --set crds.keep=false +``` + +::: + + + + +To remove the CRDs using `kubectl`, run the following: + +```bash +kubectl delete crd embeddingservers.toolhive.stacklok.dev +kubectl delete crd mcpexternalauthconfigs.toolhive.stacklok.dev +kubectl delete crd mcptoolconfigs.toolhive.stacklok.dev +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 +``` + + + + +If you created the `toolhive-system` namespace with Helm's `--create-namespace` +flag, delete it manually: + +```bash +kubectl delete namespace toolhive-system +``` + +## Next steps + +See [Run MCP servers in Kubernetes](./run-mcp-k8s.mdx) to learn how to create +and manage MCP servers using the ToolHive operator in your Kubernetes cluster. +The operator supports deploying MCPServer resources based on the deployment mode +configured during installation. + +## Related information + +- [Kubernetes introduction](./intro.mdx) - Overview of ToolHive's Kubernetes + integration +- [ToolHive operator tutorial](./quickstart.mdx) - Step-by-step tutorial for + getting started using a local kind cluster + +## Troubleshooting + +
+Authentication error with ghcr.io + +If you encounter an authentication error when pulling the Helm chart, it might +indicate a problem with your access to the GitHub Container Registry +(`ghcr.io`). + +ToolHive's charts and images are public, but if you've previously logged into +`ghcr.io` using a personal access token, you might need to re-authenticate if +your token has expired or been revoked. + +See the GitHub documentation to +[re-authenticate to the registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry#authenticating-with-a-personal-access-token-classic). + +
+ +
+Operator pod fails to start + +If the operator pod is not starting or is in a `CrashLoopBackOff` state, check +the pod logs for error messages: + +```bash +kubectl get pods -n toolhive-system +# Note the name of the toolhive-operator pod + +kubectl describe pod -n toolhive-system +kubectl logs -n toolhive-system +``` + +Common causes include: + +- **Missing CRDs**: Ensure the CRDs were installed successfully before + installing the operator. The operator requires the CRDs to function properly. +- **Configuration errors**: Check your `values.yaml` file for any + misconfigurations +- **Insufficient permissions**: Ensure your cluster has the necessary RBAC + permissions for the operator to function +- **Resource constraints**: Check if the cluster has sufficient CPU and memory + resources available +- **Image pull issues**: Verify that the cluster can pull images from `ghcr.io` + +
+ +
+CRD upgrade fails with namespace mismatch + +If you see an error like the following when upgrading the CRD chart: + +```text +Error: invalid ownership metadata; annotation validation error: +key "meta.helm.sh/release-namespace" must equal "toolhive-system": +current value is "default" +``` + +This means the CRD chart was originally installed in a different namespace than +the one you're now targeting. To fix this, patch the +`meta.helm.sh/release-namespace` annotation on all CRDs to match your desired +namespace: + +```bash +for crd in $(kubectl get crd -o name | grep toolhive.stacklok.dev); do + kubectl annotate "$crd" \ + meta.helm.sh/release-namespace= --overwrite +done +``` + +Replace `` with the namespace you want to use going forward +(for example, `toolhive-system`). This is a one-time operation. After patching, +future upgrades work as long as you use the same namespace consistently. + +
+ +
+CRDs installation fails + +If the CRDs installation fails, you might see errors about existing resources or +permission issues: + +```bash +# Check if CRDs already exist +kubectl get crd | grep toolhive + +# Remove existing CRDs if needed (this will delete all related resources) +kubectl delete crd +``` + +To reinstall the CRDs: + +```bash +helm uninstall toolhive-operator-crds +helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds +``` + +
+ +
+Namespace creation issues + +If you encounter permission errors when creating the `toolhive-system` +namespace, create it manually first: + +```bash +kubectl create namespace toolhive-system +``` + +Then install the operator without the `--create-namespace` flag: + +```bash +helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator -n toolhive-system +``` + +
+ +
+Helm chart not found + +If Helm cannot find the chart, ensure you're using the correct OCI registry URL +and that your Helm version supports OCI registries (v3.8.0+): + +```bash +# Check Helm version +helm version + +# Try pulling the chart explicitly +helm pull oci://ghcr.io/stacklok/toolhive/toolhive-operator +``` + +
+ +
+Network connectivity issues + +If you're experiencing network timeouts or connection issues: + +- Verify your cluster has internet access to reach `ghcr.io` +- Check if your organization uses a proxy or firewall that might block access +- Consider using a private registry mirror if direct access is restricted + +
diff --git a/versioned_docs/version-1.0/toolhive/guides-k8s/index.mdx b/versioned_docs/version-1.0/toolhive/guides-k8s/index.mdx new file mode 100644 index 00000000..1e16a229 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-k8s/index.mdx @@ -0,0 +1,20 @@ +--- +title: Using the ToolHive Kubernetes Operator +description: + How-to guides for using the ToolHive Kubernetes operator to run and manage MCP + servers. +--- + +import DocCardList from '@theme/DocCardList'; + +## Introduction + +The ToolHive Kubernetes operator manages MCP servers in Kubernetes clusters. It +provides a way to deploy, manage, and scale MCP servers in multi-user +environments. By defining MCP servers as Kubernetes resources, the operator +automates their deployment and management, making it easier to run MCP servers +in multi-user environments. + +## Contents + + diff --git a/versioned_docs/version-1.0/toolhive/guides-k8s/intro.mdx b/versioned_docs/version-1.0/toolhive/guides-k8s/intro.mdx new file mode 100644 index 00000000..bdfa8624 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-k8s/intro.mdx @@ -0,0 +1,69 @@ +--- +title: Overview +description: How to manage MCP servers in Kubernetes with the ToolHive operator +--- + +The ToolHive Kubernetes operator manages MCP servers in Kubernetes clusters. It +lets you define MCP servers as Kubernetes resources and automates their +deployment and management. + +:::info + +See the [ToolHive Operator quickstart tutorial](./quickstart.mdx) to get started +quickly using a local kind cluster. We'd love for you to try it out and send +feedback! + +::: + +## Overview + +The operator introduces new Custom Resource Definitions (CRDs) into your +Kubernetes cluster. The primary CRDs for MCP server workloads are `MCPServer`, +which represents a single MCP server running in Kubernetes, `MCPRemoteProxy`, +which represents an MCP server running outside the cluster that is proxied by +ToolHive, and `VirtualMCPServer`, which represents a virtual MCP server gateway +that aggregates multiple backend MCP servers. + +When you create an `MCPServer` resource, the operator automatically: + +1. Creates a Deployment to run the MCP server +2. Sets up a Service to expose the MCP server +3. Configures the appropriate permissions and settings +4. Manages the lifecycle of the MCP server + +```mermaid +flowchart TB + subgraph K8s["

Kubernetes cluster

"] + subgraph K8s1["**Deployment**"] + Svc1["HTTP Proxy
Service"] -- http --> Proxy1["HTTP Proxy
Pod"] -- stdio or http --> MCP1["MCP Server
Pod"] + Proxy1 -.->|creates| MCP1 + end + subgraph K8s2["**Deployment**"] + Svc2["HTTP Proxy
Service"] -- http --> Proxy2["HTTP Proxy
Pod"] -- stdio or http --> MCP2["MCP Server
Pod"] + Proxy2 -.->|creates| MCP2 + end + Ingress["Ingress"] -- http --> Svc1 & Svc2 + Operator["ToolHive
Operator"] -.->|creates| K8s1 & K8s2 + end + + Client["MCP Client
[ex: Copilot]"] -- http --> Ingress +``` + +`MCPRemoteProxy` and `VirtualMCPServer` resources work similarly, with the +operator managing a proxy pod that connects to remote MCP servers or aggregates +multiple backends, respectively. + +The diagram shows how clients connect to MCP servers through a standard Ingress +or Gateway. To learn how to expose your MCP servers and connect clients, see +[Connect clients to MCP servers](./connect-clients.mdx). + +## Installation + +[Deploy the ToolHive operator](./deploy-operator.mdx) in your Kubernetes +cluster. + +Once the operator is installed, you can create and manage MCP servers: + +- [Run MCP servers in Kubernetes](./run-mcp-k8s.mdx) +- [Proxy remote MCP servers](./remote-mcp-proxy.mdx) +- [Virtual MCP Server (vMCP)](../guides-vmcp/index.mdx) diff --git a/versioned_docs/version-1.0/toolhive/guides-k8s/logging.mdx b/versioned_docs/version-1.0/toolhive/guides-k8s/logging.mdx new file mode 100644 index 00000000..7a7bbb07 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-k8s/logging.mdx @@ -0,0 +1,411 @@ +--- +title: Audit logging +description: Configure and manage logging for ToolHive in Kubernetes environments +--- + +ToolHive provides structured JSON logging for MCP servers in Kubernetes, giving +you detailed operational insights and compliance audit trails. You can configure +log levels, enable audit logging for tracking MCP operations, and integrate with +common log collection systems like Fluentd, Filebeat, and Splunk. + +## Overview + +The ToolHive operator provides two types of logs: + +1. **Standard application logs** - Structured operational logs from the ToolHive + operator and proxy components +2. **Audit logs** - Security and compliance logs tracking all MCP operations + +```mermaid +flowchart TB + subgraph Sources["Log sources"] + Op["ToolHive operator"] + Proxy["HTTP proxy pods"] + MCP["MCP server pods"] + end + + subgraph Processing["Log processing"] + Stdout["Standard output
(structured JSON)"] + Audit["Audit logger
(structured JSON)"] + end + + subgraph Destinations["Log destinations"] + K8s["Kubernetes logs"] + ELK["ELK stack"] + Splunk["Splunk"] + Datadog["Datadog"] + Etc["...other collectors"] + end + + Op --> Stdout + Proxy --> Stdout & Audit + MCP --> Stdout + + Stdout --> K8s + Audit --> K8s + + K8s --> ELK & Splunk & Datadog & Etc +``` + +## Structured application logs + +ToolHive automatically outputs structured JSON logs to the standard output +(stdout) of the operator and HTTP proxy (`proxyrunner`) pods. + +All logs use a consistent format for easy parsing by log collectors: + +```json +{ + "level": "info", + "ts": 1761934317.963125, + "caller": "logger/logger.go:39", + "msg": "MCP server github started successfully" +} +``` + +### Key fields in application logs + +| Field | Type | Description | +| -------- | ------ | ------------------------------------------------ | +| `level` | string | Log level: `debug`, `info`, `warn`, `error` | +| `ts` | float | Unix timestamp with microseconds | +| `caller` | string | Source file and line number of the log statement | +| `msg` | string | Log message (exact content varies by event) | + +## Enable audit logging + +Audit logs provide detailed records of all MCP operations for security and +compliance. To enable audit logging, set the `audit.enabled` field to `true` in +your MCP server manifest: + + + + +```yaml {11-12} +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: + namespace: toolhive-system +spec: + image: + # ... other spec fields ... + + # Enable audit logging + audit: + enabled: true +``` + + + + +```yaml {11-12} +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRemoteProxy +metadata: + name: + namespace: toolhive-system +spec: + remoteUrl: + # ... other spec fields ... + + # Enable audit logging + audit: + enabled: true +``` + + + + +```yaml {11-14} +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: + namespace: toolhive-system +spec: + config: + groupRef: + + # Enable audit logging + audit: + enabled: true + includeRequestData: true + includeResponseData: true + + # ... other configs ... +``` + + + + +ToolHive writes audit logs to stdout alongside standard application logs. Your +log collector can differentiate them using the `audit_id` field or by filtering +for `"msg": "audit_event"`. + +### Audit log format + +When audit logging is enabled, each MCP operation generates a structured audit +event. For example, here is a sample audit log entry for a tool execution +request from an MCPServer resource: + +```json +{ + "time": "2024-01-01T12:00:00.123456789Z", + "level": "INFO+2", + "msg": "audit_event", + "audit_id": "550e8400-e29b-41d4-a716-446655440000", + "type": "mcp_tool_call", + "logged_at": "2024-01-01T12:00:00.123456Z", + "outcome": "success", + "component": "github-server", + "source": { + "type": "network", + "value": "10.0.1.5", + "extra": { + "user_agent": "node" + } + }, + "subjects": { + "user": "john.doe@example.com", + "user_id": "user-123" + }, + "target": { + "endpoint": "/messages", + "method": "tools/call", + "name": "search_issues", + "type": "tool" + }, + "metadata": { + "extra": { + "duration_ms": 245, + "transport": "http" + } + } +} +``` + +:::info[User information in audit logs] + +User information in the `subjects` field comes from JWT claims when OIDC +authentication is configured. The system uses the `name`, `preferred_username`, +or `email` claim (in that order) for the display name. If authentication is not +configured, the `user_id` field is set to `local`. + +::: + +#### Key fields in audit logs + +| Field | Description | +| --------------- | --------------------------------------------- | +| `audit_id` | Unique identifier for the audit event | +| `type` | Type of MCP operation (see event types below) | +| `outcome` | Result: `success` or `failure` | +| `component` | Name of the MCP server | +| `subjects.user` | User display name (from JWT claims) | +| `target.method` | MCP method called | +| `target.name` | Tool/resource name | + +#### Common audit event types + +| Event Type | Description | +| -------------------- | ------------------------- | +| `mcp_initialize` | MCP server initialization | +| `mcp_tool_call` | Tool execution request | +| `mcp_tools_list` | List available tools | +| `mcp_resource_read` | Resource access | +| `mcp_resources_list` | List available resources | + +
+Complete audit field reference + +#### Audit log fields + +| Field | Type | Description | +| ---------------------------- | ------ | ----------------------------------------------------------------------- | +| `time` | string | Timestamp when the log was generated | +| `level` | string | Log level (INFO+2 for audit events) | +| `msg` | string | Always "audit_event" for audit logs | +| `audit_id` | string | Unique identifier for the audit event | +| `type` | string | Type of MCP operation (see event types below) | +| `logged_at` | string | UTC timestamp of the event | +| `outcome` | string | Result of the operation: `success` or `failure` | +| `component` | string | Name of the MCP server | +| `source` | object | Request source information | +| `source.type` | string | Source type (e.g., "network") | +| `source.value` | string | Source identifier (e.g., IP address) | +| `source.extra` | object | Additional source metadata | +| `subjects` | object | User and identity information | +| `subjects.user` | string | User display name (from JWT claims: name, preferred_username, or email) | +| `subjects.user_id` | string | User identifier (from JWT sub claim) | +| `subjects.client_name` | string | Client application name (optional, from JWT claims) | +| `subjects.client_version` | string | Client version (optional, from JWT claims) | +| `target` | object | Target resource information | +| `target.endpoint` | string | API endpoint path | +| `target.method` | string | MCP method called | +| `target.name` | string | Tool or resource name | +| `target.type` | string | Target type (e.g., "tool") | +| `metadata` | object | Additional metadata | +| `metadata.extra.duration_ms` | number | Operation duration in milliseconds | +| `metadata.extra.transport` | string | Transport protocol used | + +#### Audit event types + +| Event Type | Description | +| -------------------- | ------------------------- | +| `mcp_initialize` | MCP server initialization | +| `mcp_tool_call` | Tool execution request | +| `mcp_tools_list` | List available tools | +| `mcp_resource_read` | Resource access | +| `mcp_resources_list` | List available resources | +| `mcp_prompt_get` | Prompt retrieval | +| `mcp_prompts_list` | List available prompts | +| `mcp_notification` | MCP notifications | +| `mcp_ping` | Health check pings | +| `mcp_completion` | Request completion | + +
+ +## Set up log collection + +ToolHive outputs structured JSON logs that work with your existing log +collection infrastructure. The examples below show basic host-based +configurations for common log collectors. Adapt these patterns to match your +organization's logging setup. + +:::note[Prerequisites] + +These examples assume: + +- Container logs are available at `/var/log/pods/` +- You have a standard Kubernetes logging setup +- Deployment manifests are handled separately +- You're using host-based log collection + +::: + +:::tip[Use your existing collection methods] + +If your organization uses sidecar-based or operator-based log collection (such +as Fluent Bit sidecars or the Fluentd Operator), adapt these configuration +patterns to work with your existing infrastructure. + +::: + +### Configure Fluentd + +```text +# fluentd.conf + + @type tail + path /var/log/pods/*toolhive*.log + tag toolhive + read_from_head true + + @type json + time_key time + time_format %Y-%m-%dT%H:%M:%S.%NZ + + + +# Route standard logs + + @type elasticsearch + host elasticsearch.logging.svc.cluster.local + port 9200 + index_name toolhive + + +# Route audit logs (entries that contain audit_id) to a separate index + + @type grep + + key audit_id + pattern .+ + + @label @AUDIT + + + +``` + +### Configure Filebeat + +```yaml +filebeat.inputs: + - type: container + paths: + - /var/log/pods/*toolhive*.log + json.keys_under_root: true + json.add_error_key: true + +output.elasticsearch: + hosts: ['${ELASTICSEARCH_HOST:elasticsearch}:${ELASTICSEARCH_PORT:9200}'] + indices: + - index: 'toolhive-audit-%{+yyyy.MM.dd}' + when.has_fields: ['audit_id'] + - index: 'toolhive-%{+yyyy.MM.dd}' +``` + +### Configure Splunk + +```ini +# inputs.conf +[monitor:///var/log/pods/*toolhive*] +sourcetype = _json +index = toolhive + +# props.conf +[_json] +KV_MODE = json +SHOULD_LINEMERGE = false +TRANSFORMS-route_audit = route_audit + +# transforms.conf +[route_audit] +REGEX = "audit_id":\s*".+" +DEST_KEY = _MetaData:Index +FORMAT = toolhive_audit +``` + +## Security considerations + +Protect your log data by implementing appropriate access controls and +encryption: + +### Encrypt logs + +- Encrypt audit logs at rest and in transit +- Use TLS for log shipping to external systems + +### Restrict log access + +Implement RBAC to control who can access pod logs: + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: log-reader + namespace: toolhive-system +rules: + - apiGroups: [''] + resources: ['pods/log'] + verbs: ['get', 'list'] +``` + +## Next steps + +- Learn about [telemetry and metrics](./telemetry-and-metrics.mdx) to complement + your logging setup +- See the [observability overview](../concepts/observability.mdx) for the + complete monitoring picture +- Check the [Kubernetes CRD reference](../reference/crd-spec.md) for complete + configuration options diff --git a/versioned_docs/version-1.0/toolhive/guides-k8s/mcp-server-entry.mdx b/versioned_docs/version-1.0/toolhive/guides-k8s/mcp-server-entry.mdx new file mode 100644 index 00000000..2be428c1 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-k8s/mcp-server-entry.mdx @@ -0,0 +1,461 @@ +--- +title: Declare remote MCP server entries +description: + Register remote MCP servers as lightweight catalog entries for vMCP discovery + without deploying proxy infrastructure. +--- + +## Overview + +MCPServerEntry is a zero-infrastructure catalog entry that declares a remote MCP +server endpoint for [Virtual MCP Server (vMCP)](../guides-vmcp/index.mdx) +discovery and routing. Unlike [MCPRemoteProxy](./remote-mcp-proxy.mdx), it +creates no pods, services, or deployments. Use MCPServerEntry when you want to +include a remote server in vMCP routing without the overhead of running a proxy. + +```mermaid +flowchart LR + Client["Client"] -->|HTTP| vMCP["vMCP Gateway"] + vMCP -->|"direct (no proxy)"| RemoteMCP["Remote MCP Server"] + + subgraph K8s["Kubernetes Cluster"] + subgraph NS["toolhive-system"] + Operator["ToolHive Operator"] + vMCP + Entry["MCPServerEntry
(catalog entry only)"] + end + end + + Operator -.->|validates| Entry + vMCP -.->|discovers| Entry +``` + +MCPServerEntry is part of an +[MCPGroup](../reference/crd-spec.md#apiv1alpha1mcpgroup), which groups related +backend MCP servers together for vMCP discovery. When vMCP starts in +[discovered mode](../guides-vmcp/backend-discovery.mdx), it queries all +MCPServer, MCPRemoteProxy, and MCPServerEntry resources in the referenced group +and connects to them directly. + +## Prerequisites + +- A Kubernetes cluster (current and two previous minor versions are supported) +- Permissions to create resources in the cluster +- [`kubectl`](https://kubernetes.io/docs/tasks/tools/) configured to communicate + with your cluster +- The ToolHive operator installed in your cluster (see + [Deploy the operator](./deploy-operator.mdx)) +- A remote MCP server that supports HTTP transport (SSE or Streamable HTTP) + +## When to use MCPServerEntry vs. MCPRemoteProxy + +| | MCPServerEntry | MCPRemoteProxy | +| ------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | +| **Infrastructure** | No pods, services, or deployments | Creates a proxy pod and service | +| **Use case** | Lightweight catalog entries for well-known remote servers | Proxied connections requiring request transformation, caching, or the full middleware chain | +| **Discovery** | Discovered by VirtualMCPServer through MCPGroup membership | Discovered by VirtualMCPServer through MCPGroup membership | +| **Authentication** | Token exchange via `externalAuthConfigRef` | Full OIDC validation of incoming client requests | +| **Authorization** | Not applicable (no proxy layer) | Cedar policy enforcement on every request | +| **Audit logging** | Not applicable (no proxy layer) | Structured audit logs with user identity | +| **Telemetry** | Not applicable (no proxy layer) | OpenTelemetry tracing and Prometheus metrics | +| **SSRF protection** | Built-in URL validation blocks internal and metadata endpoints | N/A (proxy runs inside the cluster) | + +Choose MCPServerEntry when: + +- You trust the remote server and don't need per-request policy enforcement +- You want the simplest possible configuration with no workload resources (pods, + services, deployments) +- The remote server handles its own authentication + +Choose MCPRemoteProxy when: + +- You need to validate incoming client tokens with OIDC +- You need Cedar authorization policies on tool calls +- You need audit logging with user identity +- You need tool filtering or renaming at the proxy layer + +## Create an MCPServerEntry + +MCPServerEntry resources must be part of an MCPGroup. Create the group first if +it doesn't exist: + +```yaml title="my-group.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 +``` + +Then create a basic MCPServerEntry: + +```yaml title="my-entry.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServerEntry +metadata: + name: my-remote-tool + namespace: toolhive-system +spec: + groupRef: my-group + remoteURL: https://mcp.example.com/mcp + transport: streamable-http +``` + +Apply both resources: + +```bash +kubectl apply -f my-group.yaml -f my-entry.yaml +``` + +:::info[What's happening?] + +When you apply an MCPServerEntry resource: + +1. The ToolHive operator detects the new resource +2. The operator validates the spec: checks that the referenced MCPGroup exists, + validates the remote URL against SSRF patterns, and verifies any referenced + auth or TLS resources +3. The operator sets the entry's phase to `Valid` if all checks pass, or + `Failed` with a descriptive condition if something is wrong +4. When a VirtualMCPServer in + [discovered mode](../guides-vmcp/backend-discovery.mdx) starts, it discovers + the entry through its MCPGroup membership and connects directly to the remote + URL + +::: + +### Required fields + +| Field | Description | Validation | +| ----------- | ------------------------------------------ | -------------------------- | +| `remoteURL` | URL of the remote MCP server | Must match `^https?://` | +| `transport` | Transport protocol for the remote server | `sse` or `streamable-http` | +| `groupRef` | Name of the MCPGroup this entry belongs to | Required, minimum length 1 | + +## Configure authentication + +When the remote MCP server requires authentication, reference an +[MCPExternalAuthConfig](./token-exchange-k8s.mdx) resource to configure token +exchange. The MCPExternalAuthConfig must exist in the same namespace as the +MCPServerEntry. + +```yaml title="auth-entry.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPExternalAuthConfig +metadata: + name: my-auth-config + namespace: toolhive-system +spec: + type: tokenExchange + tokenExchange: + tokenUrl: https://auth.company.com/protocol/openid-connect/token + clientId: remote-mcp-client + clientSecretRef: + name: remote-mcp-secret + key: client-secret + audience: https://mcp.example.com +--- +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServerEntry +metadata: + name: internal-tool + namespace: toolhive-system +spec: + groupRef: my-group + remoteURL: https://internal-mcp.corp.example.com/mcp + transport: streamable-http + # highlight-next-line + externalAuthConfigRef: + name: my-auth-config +``` + +When vMCP discovers this entry, it uses the referenced MCPExternalAuthConfig to +perform token exchange before forwarding requests to the remote server. + +## Configure custom TLS certificates + +If the remote server uses a certificate signed by an internal CA, provide a +custom CA bundle so that vMCP can verify the TLS connection. + +First, create a ConfigMap containing the CA certificate: + +```bash +kubectl create configmap internal-ca-bundle \ + --from-file=ca.crt=/path/to/ca-certificate.pem \ + -n toolhive-system +``` + +Then reference it in the MCPServerEntry: + +```yaml title="tls-entry.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServerEntry +metadata: + name: internal-tool + namespace: toolhive-system +spec: + groupRef: my-group + remoteURL: https://internal-mcp.corp.example.com/mcp + transport: streamable-http + # highlight-start + caBundleRef: + configMapRef: + name: internal-ca-bundle + key: ca.crt + # highlight-end +``` + +## Inject custom headers + +Some remote MCP servers require custom headers for tenant identification, API +keys, or other purposes. Use the `headerForward` field to inject headers into +requests forwarded to the remote server. + +```yaml title="header-entry.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServerEntry +metadata: + name: my-remote-tool + namespace: toolhive-system +spec: + groupRef: my-group + remoteURL: https://mcp.example.com/mcp + transport: streamable-http + # highlight-start + headerForward: + addPlaintextHeaders: + X-Custom-Header: my-value + # highlight-end +``` + +For sensitive values like API keys, use `addHeadersFromSecret` instead. See the +[Inject custom headers](./remote-mcp-proxy.mdx#inject-custom-headers) section of +the MCPRemoteProxy guide for the full syntax, which MCPServerEntry shares. + +## Complete example + +This example creates the MCPServerEntry-related resources for authentication and +custom TLS. If you reference a CA bundle ConfigMap such as `partner-ca-bundle`, +it must already exist or be created separately: + +```yaml title="complete-entry.yaml" +--- +# 1. Create the MCPGroup +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPGroup +metadata: + name: engineering-tools + namespace: toolhive-system +spec: + description: Engineering team MCP servers + +--- +# 2. Create authentication config for token exchange +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPExternalAuthConfig +metadata: + name: remote-auth + namespace: toolhive-system +spec: + type: tokenExchange + tokenExchange: + tokenUrl: https://auth.company.com/protocol/openid-connect/token + clientId: remote-mcp-client + clientSecretRef: + name: remote-mcp-secret + key: client-secret + audience: https://mcp.partner.example.com + +--- +# 3. Create the MCPServerEntry +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServerEntry +metadata: + name: partner-tools + namespace: toolhive-system +spec: + groupRef: engineering-tools + remoteURL: https://mcp.partner.example.com/mcp + transport: streamable-http + externalAuthConfigRef: + name: remote-auth + caBundleRef: + configMapRef: + name: partner-ca-bundle + key: ca.crt + headerForward: + addPlaintextHeaders: + X-Tenant-ID: engineering +``` + +Apply all resources: + +```bash +kubectl apply -f complete-entry.yaml +``` + +## Check MCPServerEntry status + +To check the status of your entries: + +```bash +kubectl get mcpserverentries -n toolhive-system +``` + +The status shows the current phase of each entry: + +| Phase | Description | +| --------- | ------------------------------------------------------------------ | +| `Valid` | All validations passed and the entry is usable | +| `Pending` | Initial state before the first reconciliation | +| `Failed` | One or more referenced resources are missing or the URL is invalid | + +For more details about a specific entry: + +```bash +kubectl describe mcpserverentry partner-tools -n toolhive-system +``` + +Check the `Conditions` section for specific validation results: + +```bash +kubectl get mcpserverentry partner-tools -n toolhive-system -o yaml +``` + +## SSRF protection + +MCPServerEntry URLs are validated against Server-Side Request Forgery (SSRF) +patterns. The operator rejects URLs that target: + +- **Loopback addresses**: `127.0.0.0/8`, `::1` +- **Link-local addresses**: `169.254.0.0/16`, `fe80::/10` +- **Cloud metadata endpoints**: `169.254.169.254` (AWS, GCP, Azure) +- **Private network ranges**: `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16` + +If a URL fails SSRF validation, the entry's phase is set to `Failed` with a +condition describing the rejection reason. + +## Next steps + +- [Configure a VirtualMCPServer](../guides-vmcp/configuration.mdx) to aggregate + MCPServerEntry backends with other MCP servers +- [Set up backend discovery](../guides-vmcp/backend-discovery.mdx) to control + how vMCP finds and connects to backends +- [Configure authentication](../guides-vmcp/authentication.mdx) for + client-to-vMCP and vMCP-to-backend security + +## Related information + +- [MCPServerEntry CRD specification](../reference/crd-spec.md#apiv1alpha1mcpserverentry) - + Full MCPServerEntry field reference +- [Introduction to the Kubernetes Operator](./intro.mdx) - Overview of all + operator resource types +- [Proxy remote MCP servers](./remote-mcp-proxy.mdx) - Full-featured proxy for + remote MCP servers +- [Run MCP servers in Kubernetes](./run-mcp-k8s.mdx) - Deploy container-based + MCP servers + +## Troubleshooting + +
+MCPServerEntry stuck in Pending phase + +If an MCPServerEntry remains in `Pending` phase after creation: + +```bash +# Check the entry status +kubectl describe mcpserverentry -n toolhive-system + +# Check operator logs +kubectl logs -n toolhive-system -l app.kubernetes.io/name=toolhive-operator +``` + +Common causes: + +- **Operator not running**: Verify the ToolHive operator pod is healthy +- **RBAC issues**: The operator may not have permission to reconcile + MCPServerEntry resources + +
+ +
+MCPServerEntry in Failed phase + +If the entry's phase is `Failed`, check the conditions for the specific reason: + +```bash +kubectl get mcpserverentry -n toolhive-system \ + -o jsonpath='{.status.conditions}' | jq +``` + +Common causes: + +- **SSRF validation failure**: The `remoteURL` targets a blocked address range + (loopback, link-local, private network, or cloud metadata). Use an externally + routable URL +- **Missing MCPGroup**: The group referenced in `groupRef` doesn't exist. Create + the MCPGroup first +- **Missing MCPExternalAuthConfig**: The auth config referenced in + `externalAuthConfigRef` doesn't exist in the same namespace +- **Missing CA ConfigMap**: The ConfigMap referenced in `caBundleRef` doesn't + exist or the specified key is missing + +
+ +
+MCPServerEntry not appearing in vMCP backends + +If a `Valid` MCPServerEntry doesn't appear in the VirtualMCPServer's discovered +backends: + +```bash +# Verify the entry is Valid +kubectl get mcpserverentry -n toolhive-system + +# Check the VirtualMCPServer status +kubectl get virtualmcpserver -n toolhive-system \ + -o jsonpath='{.status.discoveredBackends}' | jq + +# Check vMCP pod logs +kubectl logs -n toolhive-system deployment/vmcp- +``` + +Common causes: + +- **Group mismatch**: The entry's `groupRef` doesn't match the + VirtualMCPServer's `config.groupRef` +- **vMCP not restarted**: Backend changes require a pod restart to be + discovered. Restart the vMCP deployment: + ```bash + kubectl rollout restart deployment vmcp- -n toolhive-system + ``` +- **Inline mode**: The VirtualMCPServer uses `outgoingAuth.source: inline`, + which doesn't discover backends at runtime. Switch to `discovered` mode or add + the backend explicitly to `config.backends` + +
+ +
+Remote server connection failures + +If vMCP discovers the entry but can't connect to the remote server: + +```bash +# Check vMCP logs for connection errors +kubectl logs -n toolhive-system deployment/vmcp- | grep -i error +``` + +Common causes: + +- **TLS certificate errors**: If the remote server uses an internal CA, add a + `caBundleRef` pointing to the CA certificate +- **Authentication failures**: Verify the MCPExternalAuthConfig references valid + credentials and the token exchange endpoint is reachable +- **Network policies**: Ensure egress from the vMCP pod to the remote server is + allowed +- **Transport mismatch**: Verify the `transport` field matches the remote + server's actual transport protocol + +
diff --git a/versioned_docs/version-1.0/toolhive/guides-k8s/quickstart.mdx b/versioned_docs/version-1.0/toolhive/guides-k8s/quickstart.mdx new file mode 100644 index 00000000..6a4f5e33 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-k8s/quickstart.mdx @@ -0,0 +1,485 @@ +--- +title: 'Quickstart: ToolHive Kubernetes Operator' +sidebar_label: Quickstart +description: + Learn how to deploy the ToolHive Kubernetes operator and use it to manage MCP + servers in a Kubernetes cluster. +--- + +import useBaseUrl from '@docusaurus/useBaseUrl'; +import ThemedImage from '@theme/ThemedImage'; + +In this tutorial, you'll learn how to deploy the ToolHive Kubernetes operator +and use it to manage MCP servers in a Kubernetes cluster. By the end, you'll +have a working operator deployment that automatically manages MCP servers using +Kubernetes resources. + +## What you'll learn + +- How to set up a local Kubernetes cluster with kind +- How to deploy the ToolHive operator to your cluster +- How to create your first MCP server using Kubernetes resources +- How to verify that your MCP server is running correctly +- How to connect an AI agent (like GitHub Copilot) to your MCP server + +## Prerequisites + +Before starting this tutorial, make sure you have: + +- [Helm](https://helm.sh/docs/intro/install/) (v3.10 minimum, v3.14+ + recommended) installed +- [`kubectl`](https://kubernetes.io/docs/tasks/tools/) installed +- [Docker](https://docs.docker.com/get-docker/) or + [Podman](https://podman-desktop.io/downloads) installed and running +- [kind](https://kind.sigs.k8s.io/docs/user/quick-start/#installation) installed +- Basic familiarity with Kubernetes concepts (pods, deployments, services) +- An MCP client (VS Code with Copilot is used in this tutorial, but you can use + any [supported client](../reference/client-compatibility.mdx)) + +## Quickstart with Task (TL;DR) + +If you want to get up and running quickly and have +[Task](https://taskfile.dev/installation/) installed, you can use our automated +setup: + +1. Clone the ToolHive repository: + + ```bash + git clone https://github.com/stacklok/toolhive.git + cd toolhive + ``` + +2. Run the automated setup: + + ```bash + task kind-with-toolhive-operator + ``` + +This creates the kind cluster, installs an nginx ingress controller, and deploys +the latest ToolHive operator image. You should see output indicating successful +cluster creation and operator deployment. Once complete, skip to +[Step 3: Create your first MCP server](#step-3-create-your-first-mcp-server) to +continue with the tutorial. + +If you prefer to understand each step or don't have Task installed, continue +with the manual setup below. + +## Step 1: Create a kind cluster + +First, create a local Kubernetes cluster using kind. This gives you a safe +environment to experiment with the ToolHive operator. + +Create a cluster named `toolhive`: + +```bash +kind create cluster --name toolhive +``` + +Verify your cluster is running: + +```bash +kubectl cluster-info +``` + +You should see output similar to this: + +```text +Kubernetes control plane is running at https://127.0.0.1:xxxxx +CoreDNS is running at https://127.0.0.1:xxxxx/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy +``` + +This confirms your cluster is running and ready for the ToolHive operator. + +:::info[What's happening?] + +Kind (Kubernetes in Docker) creates a local Kubernetes cluster using Docker +containers. This is perfect for development and testing because it's isolated +from your main system and can be easily deleted when you're done. + +::: + +## Step 2: Deploy the ToolHive operator + +Now deploy the ToolHive operator to your cluster using Helm. The operator will +watch for MCP server resources and manage their lifecycle automatically. + +First, install the operator CRDs: + +```bash +helm upgrade --install toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds +``` + +Then install the operator: + +```bash +helm upgrade --install toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator -n toolhive-system --create-namespace +``` + +Verify that the operator deployed successfully: + +```bash +kubectl get pods -n toolhive-system +``` + +You should see output similar to: + +```text +NAME READY STATUS RESTARTS AGE +toolhive-operator-xxx 1/1 Running 0 30s +``` + +If the pod shows "Running" status, your operator is ready to manage MCP servers. + +:::info[What's happening?] + +The ToolHive operator is a Kubernetes controller that watches for `MCPServer` +resources. When you create an `MCPServer` resource, the operator automatically +creates the necessary pods, services, and configurations to run that MCP server +in your cluster. + +::: + +## Step 3: Create your first MCP server + +Now for the exciting part - create an MCP server using Kubernetes resources. +You'll deploy the fetch server, which allows AI agents to retrieve web content. + +Apply the example `fetch` MCP server from the ToolHive repository: + +```bash +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/heads/main/examples/operator/mcp-servers/mcpserver_fetch.yaml +``` + +:::info[What's happening?] + +When you create an `MCPServer` resource, the ToolHive operator detects it and +automatically: + +1. Creates a deployment to run the MCP server container +2. Sets up a service to expose the server +3. Configures the necessary networking and security settings + +::: + +Check that your MCP server was created successfully: + +```bash +kubectl get mcpservers -n toolhive-system +``` + +You should see: + +```text +NAME STATUS URL AGE +fetch Running http://mcp-fetch-proxy.toolhive-system.svc.cluster.local:8080 30s +``` + +If the status is "Pending", wait a few moments and check again. If it remains +pending for a long time, see the [troubleshooting section](#troubleshooting) at +the end of this tutorial. + +## Step 4: Test your MCP server + +Verify that your MCP server is actually working. First, get the service details: + +```bash +kubectl get service mcp-fetch-proxy -n toolhive-system +``` + +Port-forward to access the service locally: + +```bash +kubectl port-forward service/mcp-fetch-proxy -n toolhive-system 8080:8080 +``` + +In another terminal, test the server: + +```bash +curl http://localhost:8080/health +``` + +You should see a response of `OK`. + +This confirms your MCP server is running and responding correctly. + +:::info[What's happening?] + +The ToolHive operator automatically creates a Kubernetes service for each MCP +server. This service provides a stable network endpoint that other applications +(like AI agents) can use to communicate with your MCP server. + +::: + +## Step 5: Connect your AI client to the MCP server + +Now that your MCP server is running in Kubernetes, connect it to an AI client +application. We'll use Visual Studio Code with GitHub Copilot as an example. + +Make sure you still have the port-forward running from Step 4. If not, restart +it in a separate terminal: + +```bash +kubectl port-forward service/mcp-fetch-proxy -n toolhive-system 8080:8080 +``` + +Configure Visual Studio Code to connect to your MCP server. Open VS Code and +access your user settings: + +1. Open the command palette (Cmd/Ctrl+Shift+P) + +2. Type "MCP: Add Server..." and select it + + + +3. Select "HTTP" as the server type + +4. Enter the server URL: `http://localhost:8080/mcp` and press Enter + +5. Enter a name for the server (e.g., "fetch") and press Enter + +6. Choose "Global" to add the server to your global settings + +7. VS Code adds the server and opens the MCP settings file. It should look like + this: + + ```json + { + "servers": { + "fetch": { + "url": "http://localhost:8080/mcp", + "type": "http" + } + }, + "inputs": [] + } + ``` + +To verify the connection, click **Start**. The indicator should change to +"Running" and show "1 tools". + + + + +Now test the connection by asking GitHub Copilot to fetch content from a +website. Open Copilot Chat in **Agent** mode and ask: "Can you fetch the content +from https://stacklok.com and summarize it for me?" + + + +GitHub Copilot should be able to use your Kubernetes-hosted MCP server to +retrieve the content and provide a summary. + + + +:::info[What's happening?] + +You're manually configuring VS Code to connect to your MCP server running in +Kubernetes. The port-forward creates a tunnel from your local machine +(port 8080) to the Kubernetes service, allowing GitHub Copilot to communicate +with the server using the Streamable HTTP protocol. + +::: + +## Step 6: Explore operator features + +Now that you have a working MCP server, explore some operator features. + +View the detailed status of your MCP server: + +```bash +kubectl describe mcpserver fetch -n toolhive-system +``` + +This shows you the current state, any events, and configuration details. + +Try updating your MCP server's resource limits by editing the resource: + +```bash +kubectl patch mcpserver fetch -n toolhive-system --type='merge' -p '{"spec":{"resources":{"limits":{"memory":"256Mi"}}}}' +``` + +You should see output confirming the patch: + +```text +mcpserver.toolhive.stacklok.dev/fetch patched +``` + +Check that the pod has been updated with the new resource limits: + +```bash +kubectl get pods -n toolhive-system -l app.kubernetes.io/instance=fetch -o jsonpath='{.items[0].spec.containers[0].resources.limits.memory}' +``` + +You should see output showing the updated memory limit: + +```text +256Mi +``` + +This demonstrates how the operator automatically updates the underlying pod when +you modify the MCPServer resource. + +## Step 7: Clean up + +When you're done experimenting, you can clean up your resources. + +Delete the MCP server: + +```bash +kubectl delete mcpserver fetch -n toolhive-system +``` + +Verify it's been removed: + +```bash +kubectl get mcpservers -n toolhive-system +``` + +You should see: + +```text +No resources found in toolhive-system namespace. +``` + +Check that the pods are also gone: + +```bash +kubectl get pods -l app.kubernetes.io/name=fetch -n toolhive-system +``` + +You should see: + +```text +No resources found in toolhive-system namespace. +``` + +:::info[What's happening?] + +When you delete an `MCPServer` resource, the operator automatically cleans up +all the associated Kubernetes resources (pods, services, etc.). This ensures no +orphaned resources are left behind. + +::: + +When you're completely finished, delete the kind cluster: + +```bash +kind delete cluster --name toolhive +``` + +:::tip[For Task users] + +If you followed the [TL;DR setup](#quickstart-with-task-tldr) using Task, you +can also run: + +```bash +task kind-destroy +``` + +This will fully remove the kind cluster and clean up all associated resources. + +::: + +## What's next? + +Congratulations! You've successfully deployed the ToolHive operator and created +your first MCP server using Kubernetes resources. You now have a working +Kubernetes environment where MCP servers are automatically managed by the +operator. + +Here are some next steps to explore: + +- Learn about + [advanced MCP server configurations](../guides-k8s/run-mcp-k8s.mdx) for + production deployments +- Learn more about [Helm deployment options](../guides-k8s/deploy-operator.mdx) + and configuration +- Integrate MCP servers with your existing Kubernetes applications +- Try deploying other MCP servers from the ToolHive registry + +## Troubleshooting + +
+Operator pod not starting + +If the operator pod isn't starting, check the logs: + +```bash +kubectl logs -n toolhive-system deployment/toolhive-operator +``` + +
+ +
+MCP server stuck in pending state + +Check the operator logs to see what's happening: + +```bash +kubectl logs -n toolhive-system deployment/toolhive-operator -f +``` + +Also check if there are any resource constraints: + +```bash +kubectl describe mcpserver fetch -n toolhive-system +``` + +
+ +
+Can't access MCP server + +Verify the service is created and has endpoints: + +```bash +kubectl get service mcp-fetch-proxy -n toolhive-system +kubectl get endpoints mcp-fetch-proxy -n toolhive-system +``` + +
diff --git a/versioned_docs/version-1.0/toolhive/guides-k8s/rate-limiting.mdx b/versioned_docs/version-1.0/toolhive/guides-k8s/rate-limiting.mdx new file mode 100644 index 00000000..cf74695f --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-k8s/rate-limiting.mdx @@ -0,0 +1,202 @@ +--- +title: Rate limiting +description: + Configure per-user and shared rate limits on MCPServer resources to prevent + noisy neighbors and protect downstream services. +--- + +Configure token bucket rate limits on MCPServer resources to control how many +tool invocations users can make. Rate limiting prevents individual users from +monopolizing shared servers and protects downstream services from traffic +spikes. + +ToolHive supports two scopes of rate limiting: + +- **Shared** limits cap total requests across all users. +- **Per-user** limits cap requests independently for each authenticated user. + +Both scopes can be applied at the server level and overridden per tool. A +request must pass all applicable limits to proceed. + +:::info[Prerequisites] + +Before you begin, ensure you have: + +- A Kubernetes cluster with the ToolHive Operator installed +- Redis deployed in your cluster — rate limiting stores token bucket counters in + Redis (see [Redis Sentinel session storage](./redis-session-storage.mdx) for + deployment instructions) +- For per-user limits: authentication enabled on the MCPServer (`oidcConfig`, + `oidcConfigRef`, or `externalAuthConfigRef`) + +If you need help with these prerequisites, see: + +- [Kubernetes quickstart](./quickstart.mdx) +- [Authentication and authorization](./auth-k8s.mdx) + +::: + +## How rate limiting works + +Rate limits use a **token bucket** algorithm. Each bucket has a capacity +(`maxTokens`) and a refill period (`refillPeriod`). The bucket starts full and +each `tools/call` request consumes one token. When the bucket is empty, requests +are rejected until tokens refill. The refill rate is `maxTokens / refillPeriod` +tokens per second. + +Only `tools/call` requests are rate-limited. Lifecycle methods (`initialize`, +`ping`) and discovery methods (`tools/list`, `prompts/list`) pass through +unconditionally. + +When a request is rejected, the proxy returns: + +- **HTTP 429** with a `Retry-After` header (seconds until a token is available) +- A **JSON-RPC error** with code `-32029` and `retryAfterSeconds` in the error + data + +If Redis is unreachable, rate limiting **fails open** and all requests are +allowed through. + +## Configure shared rate limits + +Shared limits apply a single token bucket across all users. Use them to cap +total throughput to protect downstream services. + +```yaml title="mcpserver-shared-ratelimit.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: weather-server +spec: + image: ghcr.io/stackloklabs/weather-mcp/server + transport: streamable-http + sessionStorage: + provider: redis + address: + # highlight-start + rateLimiting: + shared: + maxTokens: 1000 + refillPeriod: 1m0s + # highlight-end +``` + +This allows 1,000 total `tools/call` requests per minute across all users. + +## Configure per-user rate limits + +Per-user limits give each authenticated user their own independent token bucket. +This prevents a single user from consuming the entire server capacity. + +Per-user limits **require authentication** to be enabled. The proxy identifies +users by the `sub` claim from their JWT token. + +```yaml title="mcpserver-peruser-ratelimit.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: weather-server +spec: + image: ghcr.io/stackloklabs/weather-mcp/server + transport: streamable-http + oidcConfig: + type: inline + inline: + issuer: https://my-idp.example.com + audience: my-audience + sessionStorage: + provider: redis + address: + # highlight-start + rateLimiting: + perUser: + maxTokens: 100 + refillPeriod: 1m0s + # highlight-end +``` + +This allows each user 100 `tools/call` requests per minute independently. + +## Combine shared and per-user limits + +You can configure both scopes together. A request must pass **all** applicable +limits. This lets you set a per-user ceiling while also capping total server +throughput. + +```yaml title="mcpserver-combined-ratelimit.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: weather-server +spec: + image: ghcr.io/stackloklabs/weather-mcp/server + transport: streamable-http + oidcConfig: + type: inline + inline: + issuer: https://my-idp.example.com + audience: my-audience + sessionStorage: + provider: redis + address: + rateLimiting: + # highlight-start + shared: + maxTokens: 1000 + refillPeriod: 1m0s + perUser: + maxTokens: 100 + refillPeriod: 1m0s + # highlight-end +``` + +## Add per-tool overrides + +Individual tools can have tighter limits than the server default. Per-tool +limits are enforced **in addition to** server-level limits. + +```yaml title="mcpserver-pertool-ratelimit.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: weather-server +spec: + image: ghcr.io/stackloklabs/weather-mcp/server + transport: streamable-http + oidcConfig: + type: inline + inline: + issuer: https://my-idp.example.com + audience: my-audience + sessionStorage: + provider: redis + address: + rateLimiting: + perUser: + maxTokens: 100 + refillPeriod: 1m0s + # highlight-start + tools: + - name: expensive_search + perUser: + maxTokens: 10 + refillPeriod: 1m0s + - name: shared_resource + shared: + maxTokens: 50 + refillPeriod: 1m0s + # highlight-end +``` + +In this example: + +- Each user can make 100 total tool calls per minute. +- Each user can make at most 10 `expensive_search` calls per minute (and those + also count toward the 100 server-level limit). +- All users combined can make 50 `shared_resource` calls per minute. + +## Next steps + +- [Token exchange](./token-exchange-k8s.mdx) to configure token exchange for + upstream service authentication +- [CRD reference](../reference/crd-spec.md) for complete field definitions diff --git a/versioned_docs/version-1.0/toolhive/guides-k8s/redis-session-storage.mdx b/versioned_docs/version-1.0/toolhive/guides-k8s/redis-session-storage.mdx new file mode 100644 index 00000000..aaf3e845 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-k8s/redis-session-storage.mdx @@ -0,0 +1,602 @@ +--- +title: Redis Sentinel session storage +description: + How to deploy Redis Sentinel and configure persistent session storage for the + ToolHive embedded authorization server. +--- + +Deploy Redis Sentinel and configure it as the session storage backend for the +ToolHive embedded authorization server. By default, sessions are stored in +memory, which means upstream tokens are lost when pods restart and users must +re-authenticate. Redis Sentinel provides persistent storage with automatic +master discovery, ACL-based access control, and optional failover when replicas +are configured. + +:::info[Prerequisites] + +Before you begin, ensure you have: + +- A Kubernetes cluster with the ToolHive Operator installed +- `kubectl` configured to access your cluster +- Familiarity with the + [embedded authorization server](./auth-k8s.mdx#set-up-embedded-authorization-server-authentication) + setup + +If you need help installing the ToolHive Operator, see the +[Kubernetes quickstart guide](./quickstart.mdx). + +::: + +## Deploy Redis Sentinel + +Deploy a Redis master and a three-node Sentinel cluster. The following manifests +create the Redis and Sentinel StatefulSets with ACL authentication and +persistent storage. + +Create the `redis` namespace: + +```bash +kubectl create namespace redis +``` + +Save the following manifests to a file called `redis-sentinel.yaml`. + +The ACL Secret defines a `toolhive-auth` user with permissions restricted to the +`thv:auth:*` key pattern that ToolHive uses for session data. An init container +copies the ACL file into the Redis data directory so it persists across +restarts. + +:::tip[Generate a strong ACL password] + +Generate a random password and use it in the ACL Secret and Kubernetes Secret +below: + +```bash +openssl rand -base64 32 +``` + +In the ACL entry, the `>` prefix before the password is +[Redis ACL syntax](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/#acl-rules) +meaning "set this user's password." Replace `YOUR_REDIS_ACL_PASSWORD` with the +generated value. + +::: + +```yaml title="redis-sentinel.yaml — Redis master and ACL" +# --- Redis ACL Secret +apiVersion: v1 +kind: Secret +metadata: + name: redis-acl + namespace: redis +type: Opaque +stringData: + # highlight-start + users.acl: >- + user toolhive-auth on >YOUR_REDIS_ACL_PASSWORD ~thv:auth:* &* +GET +SET + +SETNX +DEL +EXISTS +EXPIRE +SADD +SREM +SMEMBERS +EVAL +MULTI +EXEC + +EVALSHA +PING + # highlight-end +--- +# --- Redis headless Service +apiVersion: v1 +kind: Service +metadata: + name: redis + namespace: redis +spec: + clusterIP: None + selector: + app: redis + ports: + - name: redis + port: 6379 +--- +# --- Redis master StatefulSet +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: redis + namespace: redis +spec: + serviceName: redis + replicas: 1 + selector: + matchLabels: + app: redis + template: + metadata: + labels: + app: redis + spec: + initContainers: + - name: init-acl + image: redis:7-alpine + command: ['cp', '/etc/redis-acl/users.acl', '/data/users.acl'] + volumeMounts: + - name: redis-acl + mountPath: /etc/redis-acl + - name: redis-data + mountPath: /data + containers: + - name: redis + image: redis:7-alpine + ports: + - containerPort: 6379 + command: + - redis-server + - --bind + - '0.0.0.0' + - --aclfile + - /data/users.acl + readinessProbe: + exec: + command: ['redis-cli', 'PING'] + initialDelaySeconds: 5 + periodSeconds: 5 + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 500m + memory: 512Mi + volumeMounts: + - name: redis-data + mountPath: /data + - name: redis-acl + mountPath: /etc/redis-acl + readOnly: true + volumes: + - name: redis-acl + secret: + secretName: redis-acl + volumeClaimTemplates: + - metadata: + name: redis-data + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +``` + +The next section deploys a three-node Sentinel cluster that monitors the Redis +master and handles automatic failover: + +```yaml title="redis-sentinel.yaml — Sentinel cluster (append to same file)" +# --- Sentinel configuration +apiVersion: v1 +kind: ConfigMap +metadata: + name: redis-sentinel-config + namespace: redis +data: + sentinel.conf: | + sentinel resolve-hostnames yes + sentinel announce-hostnames yes + # quorum: 2 of 3 sentinels must agree to trigger failover + sentinel monitor mymaster redis-0.redis.redis.svc.cluster.local 6379 2 + sentinel down-after-milliseconds mymaster 5000 + sentinel failover-timeout mymaster 10000 + sentinel parallel-syncs mymaster 1 +--- +# --- Sentinel headless Service +apiVersion: v1 +kind: Service +metadata: + name: redis-sentinel + namespace: redis +spec: + clusterIP: None + selector: + app: redis-sentinel + ports: + - name: sentinel + port: 26379 +--- +# --- Sentinel StatefulSet (3 replicas for quorum) +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: redis-sentinel + namespace: redis +spec: + serviceName: redis-sentinel + replicas: 3 + selector: + matchLabels: + app: redis-sentinel + template: + metadata: + labels: + app: redis-sentinel + spec: + initContainers: + - name: copy-config + image: redis:7-alpine + command: + ['cp', '/etc/sentinel-ro/sentinel.conf', '/data/sentinel.conf'] + volumeMounts: + - name: sentinel-config-ro + mountPath: /etc/sentinel-ro + - name: sentinel-data + mountPath: /data + containers: + - name: sentinel + image: redis:7-alpine + ports: + - containerPort: 26379 + name: sentinel + command: ['redis-sentinel', '/data/sentinel.conf'] + readinessProbe: + exec: + command: ['redis-cli', '-p', '26379', 'PING'] + initialDelaySeconds: 5 + periodSeconds: 5 + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 200m + memory: 256Mi + volumeMounts: + - name: sentinel-data + mountPath: /data + volumes: + - name: sentinel-config-ro + configMap: + name: redis-sentinel-config + volumeClaimTemplates: + - metadata: + name: sentinel-data + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 100Mi +``` + +Apply the manifests and wait for all pods to be ready: + +```bash +kubectl apply -f redis-sentinel.yaml +``` + +```bash +kubectl wait --for=condition=ready pod \ + -l 'app in (redis, redis-sentinel)' \ + --namespace redis \ + --timeout=300s +``` + +:::warning + +The manifests above don't disable the Redis default user, which has full access +with no password. For production deployments, add `user default off` to the +`users.acl` entry in the `redis-acl` Secret. If you disable the default user, +you must also configure Sentinel to authenticate to Redis by adding +`sentinel auth-user` and `sentinel auth-pass` to the Sentinel ConfigMap, and +update the readiness probe commands to authenticate. + +::: + +## Create Kubernetes secrets + +Create a Secret in the ToolHive namespace containing the Redis ACL credentials. +The username and password must match the ACL user defined above: + +```bash +kubectl create secret generic redis-acl-secret \ + --namespace toolhive-system \ + --from-literal=username=toolhive-auth \ + --from-literal=password="YOUR_REDIS_ACL_PASSWORD" +``` + +## Configure MCPExternalAuthConfig + +Add the `storage` block to your `MCPExternalAuthConfig` resource. The following +example shows a working configuration with Redis Sentinel storage using Sentinel +service discovery, which automatically resolves Sentinel endpoints from the +headless Service deployed above: + +```yaml title="embedded-auth-with-redis.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPExternalAuthConfig +metadata: + name: embedded-auth-server + namespace: toolhive-system +spec: + type: embeddedAuthServer + embeddedAuthServer: + issuer: 'https://mcp.example.com' + signingKeySecretRefs: + - name: auth-server-signing-key + key: signing-key + hmacSecretRefs: + - name: auth-server-hmac-secret + key: hmac-key + # highlight-start + storage: + type: redis + redis: + sentinelConfig: + masterName: mymaster + sentinelService: + name: redis-sentinel + namespace: redis + aclUserConfig: + usernameSecretRef: + name: redis-acl-secret + key: username + passwordSecretRef: + name: redis-acl-secret + key: password + # highlight-end + upstreamProviders: + - name: google + type: oidc + oidcConfig: + issuerUrl: 'https://accounts.google.com' + clientId: '' + clientSecretRef: + name: upstream-idp-secret + key: client-secret + scopes: + - openid + - profile + - email +``` + +```bash +kubectl apply -f embedded-auth-with-redis.yaml +``` + +### Using explicit Sentinel addresses + +:::note + +`sentinelAddrs` and `sentinelService` are mutually exclusive. Use +`sentinelService` when your Sentinel instances run in the same cluster, or +`sentinelAddrs` when you need to specify exact endpoints. + +::: + +Instead of service discovery, you can list Sentinel addresses explicitly. This +is useful when Sentinel instances are in a different namespace or outside the +cluster: + +```yaml title="storage block with sentinelAddrs" +storage: + type: redis + redis: + sentinelConfig: + masterName: mymaster + sentinelAddrs: + - redis-sentinel-0.redis-sentinel.redis.svc.cluster.local:26379 + - redis-sentinel-1.redis-sentinel.redis.svc.cluster.local:26379 + - redis-sentinel-2.redis-sentinel.redis.svc.cluster.local:26379 + aclUserConfig: + usernameSecretRef: + name: redis-acl-secret + key: username + passwordSecretRef: + name: redis-acl-secret + key: password +``` + +For the complete list of storage configuration fields, see the +[Kubernetes CRD reference](../reference/crd-spec.md#apiv1alpha1authserverstorageconfig). + +## Enable TLS + +Without TLS, Redis credentials and session tokens travel in plaintext between +ToolHive and Redis. You should enable TLS for any deployment beyond local +development. + +Configure the `tls` block in your storage config. ToolHive needs the CA +certificate that signed the Redis server certificate so it can verify the +connection. + +:::note + +This step only covers the ToolHive client-side TLS configuration. Your Redis and +Sentinel instances must also be configured to serve TLS — see the +[Redis TLS documentation](https://redis.io/docs/latest/operate/oss_and_stack/management/security/encryption/) +for server-side setup. + +::: + +### Create a CA certificate Secret + +Store your CA certificate in a Secret in the ToolHive namespace: + +```bash +kubectl create secret generic redis-ca-cert \ + --namespace toolhive-system \ + --from-file=ca.crt= +``` + +### Configure TLS in MCPExternalAuthConfig + +Add the `tls` block to the `redis` section of your storage config: + +```yaml title="storage block with TLS" +storage: + type: redis + redis: + sentinelConfig: + masterName: mymaster + sentinelService: + name: redis-sentinel + namespace: redis + aclUserConfig: + usernameSecretRef: + name: redis-acl-secret + key: username + passwordSecretRef: + name: redis-acl-secret + key: password + # highlight-start + tls: + caCertSecretRef: + name: redis-ca-cert + key: ca.crt + # highlight-end +``` + +When you set only `tls`, ToolHive automatically uses the same TLS configuration +for Sentinel connections. This is the recommended setup when both Redis and +Sentinel use certificates from the same CA. + +### Separate TLS config for Sentinel + +If your Sentinel instances use a different CA or require different TLS settings, +add a `sentinelTls` block: + +```yaml title="storage block with separate Sentinel TLS" +storage: + type: redis + redis: + sentinelConfig: + masterName: mymaster + sentinelService: + name: redis-sentinel + namespace: redis + aclUserConfig: + usernameSecretRef: + name: redis-acl-secret + key: username + passwordSecretRef: + name: redis-acl-secret + key: password + # highlight-start + tls: + caCertSecretRef: + name: redis-ca-cert + key: ca.crt + sentinelTls: + caCertSecretRef: + name: sentinel-ca-cert + key: ca.crt + # highlight-end +``` + +When `sentinelTls` is set, ToolHive uses separate TLS configurations for master +and Sentinel connections. Each connection type uses its own CA certificate for +verification. + +## Verify the integration + +After applying the configuration, verify that ToolHive can connect to Redis. The +examples below use `weather-server-embedded` as the MCPServer name — substitute +your own. + +Check that the MCPServer pod is running: + +```bash +kubectl get pods -n toolhive-system \ + -l app.kubernetes.io/name=weather-server-embedded +``` + +Check the proxy logs for Redis connection messages: + +```bash +kubectl logs -n toolhive-system \ + -l app.kubernetes.io/name=weather-server-embedded \ + | grep -i redis +``` + +Look for log entries that confirm a successful Redis Sentinel connection. If the +connection fails, the proxy logs contain error details. + +Test the OAuth flow end-to-end by connecting with an MCP client. After +authenticating, restart the proxy pod and verify that your session persists +without requiring re-authentication: + +```bash +# Restart the proxy pod +kubectl rollout restart deployment \ + -n toolhive-system weather-server-embedded-proxy + +# Wait for the new pod to be ready +kubectl rollout status deployment \ + -n toolhive-system weather-server-embedded-proxy +``` + +If your MCP client can continue making requests without re-authenticating, Redis +session storage is working correctly. + +## Troubleshooting + +
+Connection refused or timeout errors + +- Verify the Redis Sentinel pods are running: `kubectl get pods -n redis` +- Check that the Sentinel addresses in your config match the actual pod DNS + names: `kubectl get endpoints -n redis` +- Ensure network policies allow traffic from the `toolhive-system` namespace to + the `redis` namespace +- Verify the `masterName` matches the name in your Sentinel configuration + (`mymaster` in the example manifests above) + +
+ +
+ACL authentication failures + +- Verify the Secret exists and contains the correct credentials: + `kubectl get secret redis-acl-secret -n toolhive-system -o yaml` +- Connect to Redis directly to verify the ACL user exists: + ```bash + kubectl exec -n redis redis-0 -- redis-cli ACL LIST + ``` +- Ensure the ACL user has the required permissions (`~thv:auth:*` key pattern + and the commands listed in the ACL Secret) + +
+ +
+TLS handshake or certificate errors + +- Verify the CA certificate Secret exists in the `toolhive-system` namespace: + `kubectl get secret redis-ca-cert -n toolhive-system` +- Confirm the CA certificate matches the one that signed the Redis server + certificate +- Check proxy logs for TLS-specific errors: + ```bash + kubectl logs -n toolhive-system \ + -l app.kubernetes.io/name=weather-server-embedded \ + | grep -i "tls\|x509\|certificate" + ``` +- If using self-signed certificates for testing, you can set + `insecureSkipVerify: true` to bypass verification (not recommended for + production) +- When using separate Sentinel TLS, ensure both `tls` and `sentinelTls` are + configured with the correct CA certificates for their respective services + +
+ +
+Sessions lost after Redis failover + +- Check Sentinel logs for failover events: + `kubectl logs -n redis -l app=redis-sentinel` +- Verify that the master is reachable from Sentinel: + ```bash + kubectl exec -n redis redis-sentinel-0 -- \ + redis-cli -p 26379 SENTINEL masters + ``` +- Ensure Sentinel quorum is met (at least 2 of 3 Sentinel instances must be + running) + +
+ +## Related information + +- [Set up embedded authorization server authentication](./auth-k8s.mdx#set-up-embedded-authorization-server-authentication) +- [Backend authentication](../concepts/backend-auth.mdx) +- [Kubernetes CRD reference](../reference/crd-spec.md#apiv1alpha1authserverstorageconfig) diff --git a/versioned_docs/version-1.0/toolhive/guides-k8s/remote-mcp-proxy.mdx b/versioned_docs/version-1.0/toolhive/guides-k8s/remote-mcp-proxy.mdx new file mode 100644 index 00000000..f8978fe1 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-k8s/remote-mcp-proxy.mdx @@ -0,0 +1,962 @@ +--- +title: Proxy remote MCP servers +description: + How to deploy proxies for remote MCP servers in Kubernetes using the ToolHive + operator +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Overview + +The ToolHive operator can deploy proxies for remote MCP servers that are hosted +externally. This enables you to gain centralized observability, policy +enforcement, and audit logging for external MCP services without requiring +changes to the remote servers themselves. + +Remote MCP proxies sit between your users and external MCP servers, providing: + +- **Authentication**: Validate user tokens using OIDC +- **Authorization**: Apply Cedar policies based on user identity +- **Audit logging**: Track all requests with user identity and tool information +- **Tool filtering**: Control which tools are exposed to users +- **Telemetry**: Collect metrics and traces for monitoring + +```mermaid +flowchart LR + Client["Client"] -->|HTTP with token| Proxy["ToolHive
Remote Proxy"] + Proxy -->|HTTP| RemoteMCP["Remote MCP Server"] + + subgraph K8s["Kubernetes Cluster"] + subgraph NS["toolhive-system"] + Operator["ToolHive Operator"] + end + subgraph NS2["Proxy Namespace"] + Proxy + end + end + + Operator -.->|creates| Proxy + + IDP["Identity Provider
(Keycloak, Okta, etc.)"] -.->|validates tokens| Proxy +``` + +:::info[Experimental] + +The MCPRemoteProxy CRD is still in an alpha state so breaking changes to the +spec and its capabilities are possible. + +::: + +## Prerequisites + +- A Kubernetes cluster (current and two previous minor versions are supported) +- Permissions to create resources in the cluster +- [`kubectl`](https://kubernetes.io/docs/tasks/tools/) configured to communicate + with your cluster +- The ToolHive operator installed in your cluster (see + [Deploy the operator](./deploy-operator.mdx)) +- An OIDC identity provider (Keycloak, Okta, Azure AD, etc.) +- Access to a remote MCP server that supports HTTP transport (SSE or Streamable + HTTP) + +## Use cases + +### Enterprise SaaS MCP servers + +Organizations using external MCP services need centralized visibility and +control over how employees interact with these services. For example, a company +using a SaaS analytics platform with an MCP interface wants to: + +- Monitor which tools employees are calling +- Apply authorization policies based on user roles +- Maintain audit logs for compliance +- Filter or restrict access to sensitive operations +- Collect centralized telemetry + +By deploying a remote MCP proxy in Kubernetes, the company gains all these +capabilities without modifying the external service. + +### Multi-tenant access control + +Different teams within an organization may need different levels of access to +the same remote MCP server. Remote proxies enable you to: + +- Deploy separate proxies with different authorization policies +- Apply team-specific tool filters +- Maintain separate audit trails per team +- Scale proxy instances independently based on team usage + +## Create a remote MCP proxy + +You can create `MCPRemoteProxy` resources in namespaces based on how the +operator was deployed. + +- **Cluster mode (default)**: Create MCPRemoteProxy resources in any namespace +- **Namespace mode**: Create MCPRemoteProxy resources only in allowed namespaces + +See [Deploy the operator](./deploy-operator.mdx#operator-deployment-modes) to +learn about the different deployment modes. + +### Basic configuration + +This example creates a remote proxy for a fictional enterprise analytics MCP +server with OIDC authentication: + +```yaml title="analytics-proxy.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRemoteProxy +metadata: + name: analytics-proxy + namespace: toolhive-system +spec: + # Remote MCP server URL + remoteURL: https://mcp.analytics.example.com + + # Port to expose the proxy on + port: 8080 + + # Transport method (sse or streamable-http) + transport: streamable-http + + # OIDC authentication configuration + oidcConfig: + type: inline + inline: + # Your identity provider's issuer URL + issuer: https://auth.company.com/realms/production + # Expected audience claim in tokens + audience: analytics-mcp-proxy + # Optional: Client ID if using introspection + clientId: analytics-proxy + + # Authorization policies + authzConfig: + type: inline + inline: + policies: + # Allow all authenticated users to list tools + - | + permit( + principal, + action == Action::"list_tools", + resource + ); + # Allow users with 'analyst' role to call read-only tools + - | + permit( + principal, + action == Action::"call_tool", + resource + ) + when { + principal has groups && + principal.groups.contains("analyst") + }; + + # Audit logging + audit: + enabled: true + + # Resource limits + resources: + limits: + cpu: '500m' + memory: 512Mi + requests: + cpu: 100m + memory: 128Mi +``` + +Apply the resource: + +```bash +kubectl apply -f analytics-proxy.yaml +``` + +:::info[What's happening?] + +When you apply an `MCPRemoteProxy` resource, here's what happens: + +1. The ToolHive operator detects the new resource (if it's in an allowed + namespace) +2. The operator creates a Deployment running the ToolHive proxy +3. The operator creates a Service to expose the proxy +4. The proxy connects to your OIDC provider to fetch JWKS keys for token + validation +5. Clients can now connect through the proxy, which validates tokens, applies + policies, and forwards requests to the remote MCP server + +::: + +### Authentication configuration + +The `oidcConfig` field validates incoming tokens from users. The proxy supports +multiple authentication methods: + + + + +Inline OIDC configuration is the simplest approach and works with most identity +providers: + +```yaml {4-6} +spec: + remoteURL: https://mcp.example.com + + oidcConfig: + type: inline + inline: + issuer: https://auth.company.com/realms/production + audience: mcp-proxy + # Optional: For token introspection + clientId: mcp-proxy-client + clientSecretRef: + name: mcp-proxy-secret + key: client-secret +``` + +The proxy automatically discovers the JWKS URL from the issuer's OIDC discovery +endpoint (`/.well-known/openid-configuration`). + + + + +For more complex configurations or when you need to share OIDC settings across +multiple proxies, use a ConfigMap: + +```yaml {21-24} title="oidc-config.yaml" +apiVersion: v1 +kind: ConfigMap +metadata: + name: company-oidc-config + namespace: toolhive-system +data: + issuer: 'https://auth.company.com/realms/production' + audience: 'mcp-proxy' + clientId: 'mcp-proxy-client' +--- +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRemoteProxy +metadata: + name: analytics-proxy + namespace: toolhive-system +spec: + remoteURL: https://mcp.analytics.example.com + oidcConfig: + type: configMap + configMap: + name: company-oidc-config +``` + + + + +:::warning[Production security] + +For production deployments: + +- Always use HTTPS for the issuer URL +- Store client secrets in Kubernetes Secrets, not inline +- Use certificate validation (set `thvCABundlePath` if using custom CAs) +- Never set `insecureAllowHTTP: true` in production + +::: + +### Authorization policies + +Authorization policies are written in +[Cedar policy language](https://www.cedarpolicy.com/) and evaluated for each +request. Policies can reference claims from the validated JWT token. + +This example shows different policy patterns: + +```yaml {6-9} +spec: + remoteURL: https://mcp.example.com + oidcConfig: + # ... OIDC config ... + + authzConfig: + type: inline + inline: + policies: + # Allow all authenticated users to list tools + - | + permit( + principal, + action == Action::"list_tools", + resource + ); + + # Allow users from specific email domain + - | + permit( + principal, + action == Action::"call_tool", + resource + ) + when { + principal has email && + principal.email like "*@company.com" + }; + + # Role-based access control + - | + permit( + principal, + action == Action::"call_tool", + resource + ) + when { + principal has groups && + principal.groups.contains("admin") + }; + + # Deny destructive operations + - | + forbid( + principal, + action == Action::"call_tool", + resource + ) + when { + resource.tool in ["delete_data", "drop_table"] + }; +``` + +Available principal attributes depend on the claims in your JWT tokens. These +are examples of common JWT claims that can be referenced in policies; actual +available attributes depend on your identity provider's token configuration: + +- `sub` - Subject (User ID) +- `email` - Email address +- `name` - Display name +- `groups` - Group memberships (array) +- Custom claims from your identity provider + +### Tool filtering + +Use `MCPToolConfig` to filter or rename tools from the remote server: + +```yaml {2,31-32} title="analytics-tools.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPToolConfig +metadata: + name: analytics-tools-filter + namespace: toolhive-system +spec: + # Only expose read-only tools + toolsFilter: + - query_data + - get_report + - list_dashboards + - export_chart + + # Rename tools for clarity + toolsOverride: + query_data: + name: analytics_query_data + description: Query analytics data with custom filters + get_report: + name: analytics_get_report + description: Retrieve a generated analytics report +--- +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRemoteProxy +metadata: + name: analytics-proxy + namespace: toolhive-system +spec: + remoteURL: https://mcp.analytics.example.com + # ... other config ... + toolConfigRef: + name: analytics-tools-filter +``` + +See [Customize tools](./customize-tools.mdx) for more information about tool +filtering and renaming. + +### Token exchange + +When your organization has federation with the remote service provider, you can +configure token exchange to convert company tokens into service-specific tokens: + +First, create an `MCPExternalAuthConfig`: + +```yaml title="external-auth-config.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPExternalAuthConfig +metadata: + name: analytics-token-exchange + namespace: toolhive-system +spec: + type: tokenExchange + tokenExchange: + tokenURL: https://auth.company.com/protocol/openid-connect/token + clientID: token-exchange-client + clientSecretRef: + name: token-exchange-creds + key: client-secret + audience: https://mcp.analytics.example.com + scopes: + - 'analytics:read' + - 'analytics:write' +``` + +Then reference it in your proxy: + +```yaml {11-12} +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRemoteProxy +metadata: + name: analytics-proxy + namespace: toolhive-system +spec: + remoteURL: https://mcp.analytics.example.com + oidcConfig: + # ... Company IDP config ... + + externalAuthConfigRef: + name: analytics-token-exchange + + # ... other config ... +``` + +Now the proxy exchanges validated company tokens for remote service tokens +before forwarding requests. + +:::tip[AWS services] + +For AWS services like the AWS MCP Server, use `type: awsSts` instead of +`type: tokenExchange`. This exchanges OIDC tokens for temporary AWS credentials +via `AssumeRoleWithWebIdentity` and signs requests with SigV4. See the +[AWS STS integration tutorial](../integrations/aws-sts.mdx) for details. + +::: + +### Inject custom headers + +Some remote MCP servers require custom headers for tenant identification, API +keys, or other purposes. Use the `headerForward` field to inject headers into +every request forwarded to the remote server. + +For non-sensitive values like tenant IDs or correlation headers, use +`addPlaintextHeaders`: + +```yaml {6-9} +spec: + remoteURL: https://mcp.analytics.example.com + # ... other config ... + + headerForward: + addPlaintextHeaders: + X-Tenant-ID: 'tenant-123' + X-Correlation-ID: 'corr-abc-def-456' +``` + +For sensitive values like API keys, reference Kubernetes Secrets using +`addHeadersFromSecret`. First, create a Secret containing the header value: + +```yaml title="api-key-secret.yaml" +apiVersion: v1 +kind: Secret +metadata: + name: api-key-secret + namespace: toolhive-system +type: Opaque +stringData: + api-key: 'your-api-key-value' +``` + +Then reference the Secret in your MCPRemoteProxy: + +```yaml title="analytics-proxy.yaml" {12-17} +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRemoteProxy +metadata: + name: analytics-proxy + namespace: toolhive-system +spec: + remoteURL: https://mcp.analytics.example.com + # ... other config ... + + headerForward: + addHeadersFromSecret: + - headerName: 'X-API-Key' + valueSecretRef: + name: api-key-secret + key: api-key +``` + +You can combine plaintext and secret-backed headers: + +```yaml {6-14} +spec: + remoteURL: https://mcp.analytics.example.com + # ... other config ... + + headerForward: + addPlaintextHeaders: + X-Tenant-ID: 'tenant-123' + X-Request-Source: 'toolhive-proxy' + addHeadersFromSecret: + - headerName: 'X-API-Key' + valueSecretRef: + name: api-key-secret + key: api-key +``` + +:::warning[Security considerations] + +- Plaintext header values are visible when you inspect the full resource (e.g., + `kubectl get ... -o yaml` or `kubectl describe`). For sensitive values (API + keys, tokens), always use `addHeadersFromSecret`. +- Secret-backed header values are stored in Kubernetes Secrets and resolved at + runtime. Only secret references (not actual values) appear in ConfigMaps used + internally by ToolHive. +- Certain headers cannot be configured for security reasons, including `Host`, + `Connection`, `Transfer-Encoding`, and proxy-related headers like + `X-Forwarded-For`. + +::: + +## Quick start example + +For testing and development, you can use the public MCP specification server: + +```yaml title="mcp-spec-proxy.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRemoteProxy +metadata: + name: mcp-spec-proxy + namespace: toolhive-system +spec: + remoteURL: https://modelcontextprotocol.io/mcp + port: 8080 + transport: streamable-http + + # For testing - use your IDP's configuration + oidcConfig: + type: inline + inline: + issuer: https://auth.company.com/realms/test + audience: mcp-test + + # Simple allow-all policy for testing + authzConfig: + type: inline + inline: + policies: + - | + permit( + principal, + action, + resource + ); + + audit: + enabled: true + + resources: + limits: + cpu: '100m' + memory: 128Mi + requests: + cpu: '50m' + memory: 64Mi +``` + +Apply it and test: + +```bash +kubectl apply -f mcp-spec-proxy.yaml + +# Check status +kubectl get mcpremoteproxy -n toolhive-system + +# Port-forward to test locally +kubectl port-forward -n toolhive-system svc/mcp-mcp-spec-proxy-remote-proxy 8080:8080 + +# Get a token from your IDP and test +curl -X POST http://localhost:8080/mcp \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' +``` + +## Expose the proxy externally + +To make the proxy accessible from outside the cluster, create an Ingress +resource: + +```yaml title="proxy-ingress.yaml" +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: analytics-proxy-ingress + namespace: toolhive-system + annotations: + # Preserve the path when forwarding + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + ingressClassName: nginx + rules: + - host: analytics-mcp.company.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: mcp-analytics-proxy-remote-proxy + port: + number: 8080 + # Optional: TLS configuration + tls: + - hosts: + - analytics-mcp.company.com + secretName: analytics-proxy-tls +``` + +Apply the Ingress: + +```bash +kubectl apply -f proxy-ingress.yaml +``` + +Users can now access the proxy at `https://analytics-mcp.company.com`. + +## Check remote proxy status + +To check the status of your remote proxies: + +```bash +# List all proxies +kubectl get mcpremoteproxy -n toolhive-system + +# Get detailed information +kubectl describe mcpremoteproxy analytics-proxy -n toolhive-system + +# Check proxy logs +kubectl logs -n toolhive-system -l app.kubernetes.io/instance=analytics-proxy +``` + +The status shows: + +- **Phase**: Current state (Pending, Ready, Failed, Terminating) +- **URL**: Internal cluster URL +- **External URL**: External URL if exposed via Ingress +- **Conditions**: Detailed status conditions + +## Telemetry and observability + +### Audit logging + +When `audit.enabled: true`, the proxy logs all requests with: + +- User identity (from token claims) +- Request method and parameters +- Response status and duration +- Timestamp + +Audit logs are structured JSON written to stdout: + +```json +{ + "loggedAt": "2025-10-29T10:15:30.123Z", + "type": "mcp_tools_call", + "outcome": "success", + "source": "remote-proxy", + "component": "analytics-proxy", + "subjects": { + "user": "jane.doe@company.com", + "user_id": "user-123" + }, + "target": { + "method": "tools/call", + "name": "query_data" + }, + "metadata": { + "auditId": "550e8400-e29b-41d4-a716-446655440000", + "extra": { + "duration_ms": 234, + "transport": "http" + } + } +} +``` + +### Prometheus metrics + +Enable Prometheus metrics to monitor proxy health and usage: + +```yaml {6-8} +spec: + remoteURL: https://mcp.example.com + # ... other config ... + + telemetry: + prometheus: + enabled: true +``` + +Metrics are exposed at `/metrics` and include: + +- `toolhive_mcp_requests_total` - Total MCP requests +- `toolhive_mcp_request_duration_seconds` - Request duration +- `toolhive_mcp_tool_calls_total` - Tool call operations +- `toolhive_mcp_active_connections` - Number of active connections + +### OpenTelemetry + +For distributed tracing and metrics export: + +```yaml {6-15} +spec: + remoteURL: https://mcp.example.com + # ... other config ... + + telemetry: + openTelemetry: + enabled: true + endpoint: https://otel-collector.company.com:4317 + serviceName: analytics-mcp-proxy + tracing: + enabled: true + samplingRate: '0.1' + metrics: + enabled: true +``` + +See [Telemetry and metrics](./telemetry-and-metrics.mdx) for more information. + +## Use with Virtual MCP Server + +MCPRemoteProxy resources can be added to an MCPGroup and discovered by a +VirtualMCPServer, enabling you to combine remote MCP servers with local +container-based MCPServer resources into a single unified endpoint. + +:::caution[Current limitation] + +vMCP can discover MCPRemoteProxy backends in a group, but authentication between +vMCP and MCPRemoteProxy is not yet fully implemented. Since MCPRemoteProxy +requires `oidcConfig` to validate incoming requests, and vMCP does not currently +forward authentication tokens to backends, vMCP cannot communicate with +MCPRemoteProxy backends that require authentication. + +This limitation will be addressed in a future release. For now, MCPRemoteProxy +works best when accessed directly by clients rather than through vMCP +aggregation. + +::: + +### Add remote proxy to a group + +To include a remote proxy in an MCPGroup for future vMCP aggregation, add the +`groupRef` field to your MCPRemoteProxy spec: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRemoteProxy +metadata: + name: context7-proxy + namespace: toolhive-system +spec: + groupRef: my-group # Reference to an MCPGroup + remoteURL: https://mcp.context7.com/mcp + transport: streamable-http + port: 8080 + oidcConfig: + type: inline + inline: + issuer: https://auth.company.com + audience: context7-proxy +``` + +### Planned benefits of vMCP aggregation + +When vMCP authentication to MCPRemoteProxy is implemented, the following +benefits will be available: + +- **Unified endpoint**: Clients connect to one vMCP URL instead of multiple + proxy endpoints +- **Centralized authentication**: vMCP handles client authentication at the + entry point +- **Tool namespacing**: Tools from remote proxies are automatically prefixed to + avoid conflicts with local MCP servers +- **Unified toolset**: Combine tools from container-based servers and external + SaaS MCP services + +See [Configure vMCP servers](../guides-vmcp/configuration.mdx) for more vMCP +configuration options. + +## Next steps + +See the [Client compatibility](../reference/client-compatibility.mdx) reference +to learn how to connect to remote MCP proxies using different clients. + +Learn how to customize MCP tools using +[filters and overrides](./customize-tools.mdx). + +Discover your deployed MCP servers automatically using the +[Kubernetes registry](../guides-registry/configuration.mdx#kubernetes-registry) +feature in the ToolHive Registry Server. + +## Related information + +- [Kubernetes CRD reference](../reference/crd-spec.md#apiv1alpha1mcpremoteproxy) - + Full MCPRemoteProxy specification +- [Deploy the operator](./deploy-operator.mdx) - Install the ToolHive operator +- [Run MCP servers in Kubernetes](./run-mcp-k8s.mdx) - Deploy local MCP servers + +## Troubleshooting + +
+Proxy pod in CrashLoopBackOff + +If the proxy pod is restarting continuously: + +```bash +# Check pod status +kubectl get pods -n toolhive-system -l app.kubernetes.io/instance=analytics-proxy + +# Check pod logs +kubectl logs -n toolhive-system -l app.kubernetes.io/instance=analytics-proxy +``` + +Common causes: + +- **Invalid OIDC configuration**: Verify the issuer URL is accessible and + returns valid OIDC discovery metadata +- **Certificate validation issues**: If using a custom CA, ensure + `thvCABundlePath` is set correctly +- **Resource limits**: Check if the pod has sufficient CPU and memory + +
+ +
+401 Unauthorized errors + +If clients receive 401 errors when calling the proxy: + +```bash +# Check proxy logs for authentication errors +kubectl logs -n toolhive-system -l app.kubernetes.io/instance=analytics-proxy | grep -i "auth" +``` + +Common causes: + +- **Missing audience claim**: Ensure tokens include the expected `aud` claim +- **Token expired**: Check token expiration time +- **Issuer mismatch**: Verify token `iss` claim matches `oidcConfig.issuer` +- **JWKS fetch failure**: Check proxy can reach the OIDC provider's JWKS + endpoint + +Verify your token claims: + +```bash +# Decode JWT payload (requires jq) +# This handles base64url and missing padding +echo "" | cut -d. -f2 | tr '_-' '/+' | awk '{l=length($0)%4; if(l>0) printf "%s", substr("====",1,4-l); print $0}' | base64 -d 2>/dev/null | jq +``` + +:::note + +JWTs use base64url encoding and may omit padding characters (`=`). The command +above converts base64url to standard base64 and adds padding if needed. If you +get an "invalid input" error, check your shell and base64 version, or manually +add padding to the payload. + +::: + +
+ +
+Remote server unreachable + +If the proxy cannot connect to the remote MCP server: + +```bash +# Check proxy logs +kubectl logs -n toolhive-system -l app.kubernetes.io/instance=analytics-proxy + +# Test connectivity from the pod +kubectl exec -n toolhive-system -- \ + curl -v https://mcp.analytics.example.com +``` + +Common causes: + +- **Network policies**: Check if network policies allow egress to the remote + server +- **DNS resolution**: Verify the remote URL resolves correctly +- **Firewall rules**: Ensure the cluster can reach the remote server +- **Certificate issues**: If using HTTPS, verify certificates are valid + +
+ +
+Tool filtering not working + +If tool filtering isn't being applied: + +```bash +# Check if MCPToolConfig exists +kubectl get mcptoolconfig -n toolhive-system + +# Verify the reference in MCPRemoteProxy +kubectl get mcpremoteproxy analytics-proxy -n toolhive-system -o yaml | \ + grep -A2 toolConfigRef +``` + +Ensure: + +- The MCPToolConfig exists in the same namespace as the MCPRemoteProxy +- The `toolConfigRef.name` matches the MCPToolConfig name +- The MCPRemoteProxy status shows the config is applied + +
+ +
+Token exchange failing + +If token exchange is configured but not working: + +```bash +# Check for token exchange errors in logs +kubectl logs -n toolhive-system -l app.kubernetes.io/instance=analytics-proxy | \ + grep -i "token.*exchange" +``` + +Verify: + +- The MCPExternalAuthConfig exists and is correctly configured +- The client secret is valid and stored in a Kubernetes Secret +- The token exchange endpoint is reachable from the proxy +- The company IDP is configured for token exchange (RFC 8693) + +
+ +
+Getting more debug information + +For additional debugging: + +```bash +# Get all resources related to your proxy +kubectl get all -n toolhive-system -l app.kubernetes.io/instance=analytics-proxy + +# Export MCPRemoteProxy for inspection +kubectl get mcpremoteproxy analytics-proxy -n toolhive-system -o yaml + +# Check operator logs +kubectl logs -n toolhive-system -l app.kubernetes.io/name=toolhive-operator + +# Check events +kubectl get events -n toolhive-system --sort-by='.lastTimestamp' | \ + grep analytics-proxy +``` + +
diff --git a/versioned_docs/version-1.0/toolhive/guides-k8s/run-mcp-k8s.mdx b/versioned_docs/version-1.0/toolhive/guides-k8s/run-mcp-k8s.mdx new file mode 100644 index 00000000..339d6f9c --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-k8s/run-mcp-k8s.mdx @@ -0,0 +1,689 @@ +--- +title: Run MCP servers in Kubernetes +description: How to deploy MCP servers in Kubernetes using the ToolHive operator +--- + +## Prerequisites + +- A Kubernetes cluster (current and two previous minor versions are supported) +- Permissions to create resources in the cluster +- [`kubectl`](https://kubernetes.io/docs/tasks/tools/) configured to communicate + with your cluster +- The ToolHive operator installed in your cluster (see + [Deploy the operator](./deploy-operator.mdx)) + +## Overview + +The ToolHive operator deploys MCP servers in Kubernetes by creating proxy pods +that manage the actual MCP server containers. + +:::tip + +If you need to build a container image for an MCP server that doesn't already +have one available, see the +[Build MCP containers](../guides-cli/build-containers.mdx) guide to learn how to +quickly create container images using the ToolHive CLI. + +::: + +### High-level architecture + +This diagram shows the basic relationship between components. The ToolHive +operator watches for `MCPServer` resources and automatically creates the +necessary infrastructure to run your MCP servers securely within the cluster. + +```mermaid +flowchart LR + Client["Client"] -->|connects| Proxy["ToolHive
Proxy/Runner"] + Proxy --> MCP["MCP Server"] + + subgraph K8s["Kubernetes Cluster"] + subgraph NS["toolhive-system"] + Operator["ToolHive Operator"] + end + subgraph NS2["MCP Server
Namespace"] + Proxy + MCP + end + end + + Operator -.->|creates| Proxy + Proxy -.->|creates| MCP +``` + +### STDIO transport flow + +For MCP servers using STDIO transport, the proxy directly attaches to the MCP +server pod's standard input/output streams. + +```mermaid +flowchart LR + subgraph Proxy["Created by Operator"] + direction TB + ProxyService["SVC: ToolHive-Proxy"] + ProxyPod["POD: ToolHive-Proxy"] + ProxyService --> ProxyPod + end + + subgraph MCP["Created by Proxy"] + direction TB + MCPPod["POD: MCP Server"] + end + + Client["Client"] -->|HTTP/SSE| Proxy + Proxy -->|Attaches/STDIO| MCP +``` + +:::info[STDIO Transport Limitation] + +MCP servers using STDIO transport support only a single client connection at a +time. If you need to support multiple users or concurrent client connections, +use SSE (Server-Sent Events) or Streamable HTTP transport instead. + +::: + +### Streamable HTTP and SSE transport flow + +For MCP servers using Server-Sent Events (SSE) or Streamable HTTP transport, the +proxy creates both a pod and a headless service. This allows direct HTTP/SSE or +HTTP/Streamable HTTP communication between the proxy and MCP server while +maintaining network isolation and service discovery. + +```mermaid +flowchart LR + subgraph Proxy["Created by Operator"] + direction TB + ProxyService["SVC: ToolHive-Proxy"] + ProxyPod["POD: ToolHive-Proxy"] + ProxyService --> ProxyPod + end + + subgraph MCP["Created by Proxy"] + direction TB + MCPService["SVC: MCP-Headless"] + MCPPod["POD: MCP Server"] + end + + Client["Client"] -->|HTTP| Proxy + Proxy -->|HTTP| MCP + MCPService --> MCPPod +``` + +## Create an MCP server + +You can create `MCPServer` resources in namespaces based on how the operator was +deployed. + +- **Cluster mode (default)**: Create MCPServer resources in any namespace +- **Namespace mode**: Create MCPServer resources only in allowed namespaces + +See [Deploy the operator](./deploy-operator.mdx#operator-deployment-modes) to +learn about the different deployment modes. + +To create an MCP server, define an `MCPServer` resource and apply it to your +cluster. This minimal example creates the +[`osv` MCP server](https://github.com/StacklokLabs/osv-mcp) which queries the +[Open Source Vulnerability (OSV) database](https://osv.dev/) for vulnerability +information. + +```yaml title="my-mcpserver.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: osv + namespace: my-namespace # Update with your namespace +spec: + image: ghcr.io/stackloklabs/osv-mcp/server + transport: streamable-http + mcpPort: 8080 + proxyPort: 8080 + resources: + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' +``` + +Apply the resource: + +```bash +kubectl apply -f my-mcpserver.yaml +``` + +:::info[What's happening?] + +When you apply an `MCPServer` resource, here's what happens: + +1. The ToolHive operator detects the new resource (if it's in an allowed + namespace) +2. The operator automatically creates the necessary RBAC resources in the target + namespace: + - A ServiceAccount with the same name as the MCPServer + - A Role with minimal permissions for StatefulSets, Services, Pods, and Pod + logs/attach operations + - A RoleBinding that connects the ServiceAccount to the Role +3. The operator creates a new Deployment containing a ToolHive proxy pod and + service to handle client connections +4. The proxy creates the actual `MCPServer` pod containing your specified + container image +5. For STDIO transport, the proxy attaches directly to the pod; for SSE and + Streamable HTTP transport, a headless service is created for direct pod + communication +6. Clients can now connect through the service → proxy → MCP server chain to use + the tools and resources (note: external clients will need an ingress + controller or similar mechanism to access the service from outside the + cluster) + +::: + +For more examples of `MCPServer` resources, see the +[example MCP server manifests](https://github.com/stacklok/toolhive/tree/main/examples/operator/mcp-servers) +in the ToolHive repo. + +## Automatic RBAC management + +The ToolHive operator automatically handles RBAC (Role-Based Access Control) for +each MCPServer instance, providing better security isolation and multi-tenant +support. Here's what the operator creates automatically: + +- **ServiceAccount**: A dedicated ServiceAccount with the same name as your + MCPServer +- **Role**: A namespace-scoped Role with minimal permissions for: + - StatefulSets (create, get, list, watch, update, patch, delete) + - Services (create, get, list, watch, update, patch, delete) + - Pods (get, list, watch) + - Pod logs and attach operations (get, list) +- **RoleBinding**: Connects the ServiceAccount to the Role + +This approach provides: + +- Each MCPServer operates with its own minimal set of permissions +- No manual RBAC setup required +- Better security isolation between different MCPServer instances +- Support for multi-tenant deployments across different namespaces + +## Customize server settings + +You can customize the MCP server by adding additional fields to the `MCPServer` +resource. The full specification is available in the +[Kubernetes CRD reference](../reference/crd-spec.md#apiv1alpha1mcpserver). + +Below are some common configurations. + +### Customize the MCP server pod + +You can customize the MCP server pod that gets created by the proxy using the +`podTemplateSpec` field. This gives you full control over the pod specification, +letting you set security contexts, resource limits, node selectors, and other +pod-level configurations. + +The `podTemplateSpec` field follows the standard Kubernetes +[`PodTemplateSpec`](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-template-v1/#PodTemplateSpec) +format, so you can use any valid pod specification options. + +This example sets resource limits. + +```yaml {14-15} title="my-mcpserver-custom-pod.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: fetch + namespace: development # Can be any namespace +spec: + image: ghcr.io/stackloklabs/gofetch/server + transport: streamable-http + mcpPort: 8080 + proxyPort: 8080 + podTemplateSpec: + spec: + containers: + - name: mcp # This name must be "mcp" + resources: # These resources apply to the MCP container + limits: + cpu: '500m' + memory: '512Mi' + requests: + cpu: '100m' + memory: '128Mi' + resources: # These resources apply to the proxy container + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' +``` + +:::info[Container name requirement] + +When customizing containers in `podTemplateSpec`, you must use `name: mcp` for +the main container. This ensures the proxy can properly manage the MCP server +process. + +::: + +### Run a server with secrets + +When your MCP servers require authentication tokens or other secrets, ToolHive +supports multiple secrets management methods to fit your existing +infrastructure. Choose the method that best suits your needs: + + + + +ToolHive can reference existing Kubernetes secrets to inject sensitive data into +your MCP server pods as environment variables. This example demonstrates how to +pass a GitHub personal access token to the `github` MCP server. + +First, create the secret. The secret must exist in the same namespace as your +MCP server and the key must match what you specify in the `MCPServer` resource. + +```bash +kubectl -n production create secret generic github-token --from-literal=token= +``` + +Next, define the `MCPServer` resource to reference the secret: + +```yaml {10-13} title="my-mcpserver-with-secrets.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: github + namespace: production # Can be any namespace +spec: + image: ghcr.io/github/github-mcp-server + transport: stdio + proxyPort: 8080 + secrets: + - name: github-token + key: token + targetEnvName: GITHUB_PERSONAL_ACCESS_TOKEN +``` + +Finally, apply the MCPServer resource: + +```bash +kubectl apply -f my-mcpserver-with-secrets.yaml +``` + + + + +[External Secrets Operator](https://external-secrets.io/) is a Kubernetes +operator that integrates external secret management systems and syncs secrets +into Kubernetes as native resources. This example demonstrates how to use +ESO-managed secrets with your MCP server. + +:::note + +When you use the External Secrets Operator, your MCP server definition will look +the same as the Kubernetes-native example. This is because the External Secrets +Operator creates standard Kubernetes secrets from external sources. + +::: + +First, create a secret using the +[ExternalSecret resource](https://external-secrets.io/latest/api/externalsecret/). +The exact configuration depends on your external secret management system. The +secret must exist in the same namespace as your MCP server and the key must +match what you specify in the `MCPServer` resource. + +Next, define the `MCPServer` resource to reference the secret: + +```yaml {10-13} title="my-mcpserver-with-secrets-eso.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: github + namespace: production # Can be any namespace +spec: + image: ghcr.io/github/github-mcp-server + transport: stdio + proxyPort: 8080 + secrets: + - name: github-token + key: token + targetEnvName: GITHUB_PERSONAL_ACCESS_TOKEN +``` + +Finally, apply the MCPServer resource: + +```bash +kubectl apply -f my-mcpserver-with-secrets-eso.yaml +``` + + + + +HashiCorp Vault provides multiple integration methods for Kubernetes +environments: + +1. [Vault Sidecar Agent Injector](https://developer.hashicorp.com/vault/docs/deploy/kubernetes/injector), + which injects a sidecar container into your pod to fetch and renew secrets +2. [Vault Secrets Operator](https://developer.hashicorp.com/vault/docs/deploy/kubernetes/vso), + which creates Kubernetes secrets from Vault secrets (similar to the External + Secrets Operator) +3. [Vault CSI Provider](https://developer.hashicorp.com/vault/docs/deploy/kubernetes/csi), + which mounts secrets directly into your pod as files + +ToolHive supports the first two methods. When you use the Vault Secrets +Operator, your MCP server definition will look the same as the Kubernetes-native +example because the Vault Secrets Operator creates standard Kubernetes secrets +from Vault. + +The Vault Sidecar Agent Injector requires additional configuration in your +`MCPServer` resource to add the required annotations. For a complete example, +see the [HashiCorp Vault integration tutorial](../integrations/vault.mdx). + + + + +### Mount a volume + +You can mount volumes into the MCP server pod to provide persistent storage or +access to data. This is useful for MCP servers that need to read/write files or +access large datasets. + +To do this, add a standard `volumes` field to the `podTemplateSpec` in the +`MCPServer` resource and a `volumeMounts` section in the container +specification. Here's an example that mounts a persistent volume claim (PVC) to +the `/projects` path in the Filesystem MCP server. The PVC must already exist in +the same namespace as the MCPServer. + +```yaml {12-15,19-22} title="my-mcpserver-with-volume.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: filesystem + namespace: data-processing # Can be any namespace +spec: + image: docker.io/mcp/filesystem + transport: stdio + proxyPort: 8080 + podTemplateSpec: + spec: + volumes: + - name: my-mcp-data + persistentVolumeClaim: + claimName: my-mcp-data-claim + containers: + - name: mcp + # ... other container settings ... + volumeMounts: + - mountPath: /projects/my-mcp-data + name: my-mcp-data + readOnly: true +``` + +## Check MCP server status + +To check the status of your MCP servers in a specific namespace: + +```bash +kubectl -n get mcpservers +``` + +To check MCP servers across all namespaces: + +```bash +kubectl get mcpservers --all-namespaces +``` + +The status, URL, and age of each MCP server is displayed. + +For more details about a specific MCP server: + +```bash +kubectl -n describe mcpserver +``` + +## Next steps + +Learn how to [connect clients to your MCP servers](./connect-clients.mdx) from +outside the cluster using Ingress or Gateway API, or from applications running +within the cluster. See the +[Client compatibility](../reference/client-compatibility.mdx) reference for +information about supported MCP clients. + +Learn how to customize MCP tools using +[filters and overrides](./customize-tools.mdx). + +Collect telemetry data from your MCP servers by following the +[Telemetry and metrics](./telemetry-and-metrics.mdx) guide. Configure audit +logging by following the [Set up logging](./logging.mdx) guide. + +Discover your deployed MCP servers automatically using the +[Kubernetes registry](../guides-registry/configuration.mdx#kubernetes-registry) +feature in the ToolHive Registry Server. + +## Related information + +- [Kubernetes CRD reference](../reference/crd-spec.md#apiv1alpha1mcpserver) - + Reference for the `MCPServer` Custom Resource Definition (CRD) +- [Deploy the operator](./deploy-operator.mdx) - Install the ToolHive operator +- [Build MCP containers](../guides-cli/build-containers.mdx) - Create custom MCP + server container images + +## Troubleshooting + +
+MCPServer resource not creating pods + +If your `MCPServer` resource is created but no pods appear, first ensure you +created the `MCPServer` resource in an allowed namespace. If the operator runs +in namespace mode and you didn't include the namespace in the +`allowedNamespaces` list, the operator ignores the resource. Check the +operator's configuration: + +```bash +helm get values toolhive-operator -n toolhive-system +``` + +Check the `operator.rbac.scope` and `operator.rbac.allowedNamespaces` +properties. If the operator runs in `namespace` mode, add the namespace where +you created the `MCPServer` to the `allowedNamespaces` list. See +[Operator deployment modes](./deploy-operator.mdx#operator-deployment-modes). + +If the operator runs in `cluster` mode (default) or the `MCPServer` is in an +allowed namespace, check the operator logs and resource status: + +```bash +# Check MCPServer status +kubectl -n describe mcpserver + +# Check operator logs +kubectl -n toolhive-system logs -l app.kubernetes.io/name=toolhive-operator + +# Verify the operator is running +kubectl -n toolhive-system get pods -l app.kubernetes.io/name=toolhive-operator +``` + +Other common causes include: + +- **Operator not running**: Ensure the ToolHive operator is deployed and running +- **Invalid image reference**: Verify the container image exists and is + accessible +- **RBAC issues**: The operator automatically creates RBAC resources, but check + for cluster-level permission issues +- **Resource quotas**: Check if namespace resource quotas prevent pod creation + +
+ +
+MCP server pod fails to start + +If the MCP server pod is created but fails to start or is in `CrashLoopBackOff`: + +```bash +# Check pod status +kubectl -n get pods + +# Describe the failing pod +kubectl -n describe pod + +# Check pod logs +kubectl -n logs -c mcp +``` + +Common causes include: + +- **Image pull errors**: Verify the container image is accessible and the image + name is correct +- **Missing secrets**: Ensure required secrets exist and are properly referenced +- **Resource constraints**: Check if the pod has sufficient CPU and memory + resources +- **Permission issues**: Verify the security context and RBAC permissions are + correctly configured +- **Invalid arguments**: Check if the `args` field contains valid arguments for + the MCP server + +
+ +
+Proxy pod connection issues + +If the proxy pod is running but clients cannot connect: + +```bash +# Check proxy pod status +kubectl -n get pods -l app.kubernetes.io/instance= + +# Check proxy logs +kubectl -n logs -l app.kubernetes.io/instance= + +# Verify service is created +kubectl -n get services +``` + +Common causes include: + +- **Service not created**: Ensure the proxy service exists and has the correct + selectors +- **Port configuration**: Verify the `port` field matches the MCP server's + listening port +- **Transport mismatch**: Ensure the `transport` field + (stdio/sse/streamable-http) matches the MCP server's capabilities +- **Network policies**: Check if network policies are blocking communication + +
+ +
+Secret mounting issues + +If secrets are not being properly mounted or environment variables are missing: + +```bash +# Check if secret exists +kubectl -n get secret + +# Verify secret content +kubectl -n describe secret + +# Check environment variables in the pod +kubectl -n exec -c mcp -- env | grep +``` + +Common causes include: + +- **Secret doesn't exist**: Create the secret in the correct namespace +- **Wrong key name**: Ensure the `key` field matches the actual key in the + secret +- **Namespace mismatch**: Secrets must be in the same namespace as the + `MCPServer` +- **Permission issues**: The operator automatically creates the necessary RBAC + resources, but verify the ServiceAccount has access to read secrets + +
+ +
+Volume mounting problems + +If persistent volumes or other volumes are not mounting correctly: + +```bash +# Check PVC status +kubectl -n get pvc + +# Describe the PVC +kubectl -n describe pvc + +# Check volume mounts in the pod +kubectl -n describe pod +``` + +Common causes include: + +- **PVC not bound**: Ensure the PersistentVolumeClaim is bound to a + PersistentVolume +- **Namespace mismatch**: The PVC must be in the same namespace as the MCPServer +- **Storage class issues**: Verify the storage class exists and is available +- **Access mode conflicts**: Check that the access mode is compatible with your + setup +- **Mount path conflicts**: Ensure mount paths don't conflict with existing + directories + +
+ +
+Resource limit issues + +If pods are being killed due to resource constraints: + +```bash +# Check resource usage +kubectl -n top pods + +# Check for resource limit events +kubectl -n get events --sort-by='.lastTimestamp' + +# Describe the pod for resource information +kubectl -n describe pod +``` + +Solutions: + +- **Increase resource limits**: Adjust `resources.limits` in the `MCPServer` + spec +- **Optimize resource requests**: Set appropriate `resources.requests` values +- **Check node capacity**: Ensure cluster nodes have sufficient resources +- **Review resource quotas**: Check namespace resource quotas and limits + +
+ +
+Debugging connectivity + +To test connectivity between components: + +```bash +# Port-forward to test direct access to the proxy +kubectl -n port-forward service/ 8080:8080 + +# Test the connection locally +curl http://localhost:8080/health + +# Check service endpoints +kubectl -n get endpoints +``` + +
+ +
+Getting more debug information + +For additional debugging information: + +```bash +# Get all resources related to your MCP server +kubectl -n get all -l app.kubernetes.io/instance= + +# Check operator events +kubectl -n get events --field-selector involvedObject.kind=MCPServer + +# Export MCPServer resource for inspection +kubectl -n get mcpserver -o yaml +``` + +
diff --git a/versioned_docs/version-1.0/toolhive/guides-k8s/telemetry-and-metrics.mdx b/versioned_docs/version-1.0/toolhive/guides-k8s/telemetry-and-metrics.mdx new file mode 100644 index 00000000..5fb2d452 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-k8s/telemetry-and-metrics.mdx @@ -0,0 +1,286 @@ +--- +title: Telemetry and metrics +description: How to enable OpenTelemetry (metrics and traces) and Prometheus + instrumentation for ToolHive MCP servers inside of Kubernetes using the + ToolHive Operator +--- + +ToolHive includes built-in instrumentation using OpenTelemetry, which gives you +comprehensive observability for your MCP server interactions. You can export +traces and metrics to popular observability backends like Jaeger, Honeycomb, +Datadog, and Grafana Cloud, or expose Prometheus metrics directly. + +## What you can monitor + +ToolHive's telemetry captures detailed information about MCP interactions +including traces, metrics, and performance data. For a comprehensive overview of +the telemetry architecture, metrics collection, and monitoring capabilities, see +the [observability overview](../concepts/observability.mdx). + +## Enable telemetry + +You can enable telemetry when deploying an MCP server by specifying Telemetry +configuration in the MCPServer or MCPRemoteProxy custom resource. + +This example runs the Fetch MCP server and exports traces to a deployed instance +of the [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/): + +```yaml {12} +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer # or MCPRemoteProxy +metadata: + name: gofetch + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/gofetch/server + transport: streamable-http + proxyPort: 8080 + mcpPort: 8080 + # ... other spec fields ... + telemetry: + openTelemetry: + enabled: true + endpoint: otel-collector-opentelemetry-collector.monitoring.svc.cluster.local:4318 + serviceName: mcp-fetch-server + insecure: true + metrics: + enabled: true + tracing: + enabled: true + samplingRate: '0.05' + prometheus: + enabled: true +``` + +The `spec.telemetry.openTelemetry.endpoint` will be the OpenTelemetry collector +that is deployed inside of your infrastructure, the +`spec.telemetry.openTelemetry.serviceName` will be what you can use to identify +your MCP server in your observability stack. + +### Export metrics to an OTLP endpoint + +If you want to enable ToolHive to export metrics to your OTel collector, you can +enable the `spec.telemetry.openTelemetry.metrics.enabled` flag. + +### Export traces to an OTLP endpoint + +If you want to enable ToolHive to export tracing information, you can enable the +`spec.telemetry.openTelemetry.tracing.enabled` flag. + +You can also set the sampling rate of your traces by setting the +`spec.telemetry.openTelemetry.tracing.sampleRate` option to a number between 0 +and 1.0. By default this will be `0.05` which equates to 5% of all requests. + +:::note + +The `spec.telemetry.openTelemetry.endpoint` is provided as a hostname and +optional port, without a scheme or path (e.g., use `api.honeycomb.io` or +`api.honeycomb.io:443`, not `https://api.honeycomb.io`). ToolHive automatically +uses HTTPS unless `--otel-insecure` is specified. + +::: + +By default, the service name is set to `toolhive-mcp-proxy`, and the sampling +rate is `0.05` (5%). + +:::tip[Recommendation] + +Set the `spec.telemetry.openTelemetry.serviceName` flag to a meaningful name for +each MCP server. This helps you identify the server in your observability +backend. + +::: + +### Enable Prometheus metrics + +You can expose Prometheus-style metrics at `/metrics` on the main transport port +for local scraping by enabling the `spec.telemetry.prometheus.enabled` flag. + +To access the metrics, you can use `curl` or any Prometheus-compatible scraper. +The metrics are available at `http://:/metrics`, where `` is +resolvable address of the ToolHive ProxyRunner fronting your MCP server pod and +`` is the port of which the ProxyRunner service is configured to expose +for traffic. + +### Dual export + +You can export to both an OTLP endpoint and expose Prometheus metrics +simultaneously. + +The `MCPServer` example at the top of this page has dual export enabled. + +## Observability backends + +ToolHive can export telemetry data to many different observability backends. It +supports exporting traces and metrics to any backend that implements the OTLP +protocol. Some common examples are listed below, but specific configurations +will vary based on your environment and requirements. + +### OpenTelemetry Collector (recommended) + +The OpenTelemetry Collector is a vendor-agnostic way to receive, process and +export telemetry data. It supports many backend services, scalable deployment +options, and advanced processing capabilities. + +```mermaid +graph LR + A[ToolHive] -->|traces & metrics| B[OpenTelemetry Collector] + B --> C[AWS CloudWatch] + B --> D[Splunk] + B --> E[New Relic] + B <--> F[Prometheus] + B --> G[Other OTLP backends] +``` + +You can run the OpenTelemetry Collector inside of a Kubernetes cluster, follow +the +[OpenTelemetry Collector documentation](https://opentelemetry.io/docs/collector/) +for more information. + +To export data to a local OpenTelemetry Collector, set your OTLP endpoint to the +OTLP http receiver port (default is `4318`): + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: gofetch + namespace: toolhive-system +spec: + ... + ... + telemetry: + openTelemetry: + enabled: true + endpoint: otel-collector-opentelemetry-collector.monitoring.svc.cluster.local:4318 + serviceName: mcp-fetch-server + insecure: true + metrics: + enabled: true +``` + +### Prometheus + +To collect metrics using Prometheus, run your MCP server with the +`spec.telemetry.prometheus.enabled` flag enabled and add the following to your +Prometheus configuration: + +```yaml title="prometheus.yml" +scrape_configs: + - job_name: 'toolhive-mcp-proxy' + static_configs: + - targets: [':'] + scrape_interval: 15s + metrics_path: /metrics +``` + +You can add multiple MCP servers to the `targets` list. Replace +`` with the ProxyRunner SVC name and +`` with the port number exposed by the SVC. + +### Jaeger + +[Jaeger](https://www.jaegertracing.io) is a popular open-source distributed +tracing system. You can run it inside of a Kubernetes cluster in order to store +tracing telemetry data exported by the ToolHive proxy. + +You can export traces to Jaeger by setting the OTLP endpoint to an OpenTelemetry +collector, and then configuring the collector to export tracing data to Jaeger. + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: gofetch + namespace: toolhive-system +spec: + ... + ... + telemetry: + openTelemetry: + enabled: true + endpoint: otel-collector-opentelemetry-collector.monitoring.svc.cluster.local:4318 + serviceName: mcp-fetch-server + insecure: true + tracing: + enabled: true +``` + +Inside of your OpenTelemetry collector configuration. + +```yaml +config: + receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + + exporters: + otlp/jaeger: + endpoint: http://jaeger-all-in-one-collector.monitoring:4317 + + service: + pipelines: + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp/jaeger] +``` + +### Honeycomb + +Coming soon. + +You'll need your Honeycomb API key, which you can find in your +[Honeycomb account settings](https://ui.honeycomb.io/account). + +### Datadog + +Datadog has [multiple options](https://docs.datadoghq.com/opentelemetry/) for +collecting OpenTelemetry data: + +- The + [**OpenTelemetry Collector**](https://docs.datadoghq.com/opentelemetry/setup/collector_exporter/) + is recommended for existing OpenTelemetry users or users wanting a + vendor-neutral solution. + +- The [**Datadog Agent**](https://docs.datadoghq.com/opentelemetry/setup/agent) + is recommended for existing Datadog users. + +### Grafana Cloud + +Coming soon. + +## Performance considerations + +### Sampling rates + +Adjust sampling rates based on your environment: + +- **Development**: `spec.telemetry.openTelemetry.tracing.samplingRate: 1.0` + (100% sampling) +- **Production**: `spec.telemetry.openTelemetry.tracing.samplingRate 0.01` (1% + sampling for high-traffic systems) +- **Default**: `spec.telemetry.openTelemetry.tracing.samplingRate 0.05` (5% + sampling) + +### Network overhead + +Telemetry adds minimal overhead when properly configured: + +- Use appropriate sampling rates for your traffic volume +- Monitor your observability backend costs and adjust sampling accordingly + +## Related information + +- Tutorial: + [Collect telemetry for MCP workloads](../integrations/opentelemetry.mdx) - + Step-by-step guide to set up a local observability stack +- [Telemetry and monitoring concepts](../concepts/observability.mdx) - Overview + of ToolHive's observability architecture +- [Kubernetes CRD reference](../reference/crd-spec.md#apiv1alpha1mcpserver) - + Reference for the `MCPServer` Custom Resource Definition (CRD) +- [Deploy the operator](./deploy-operator.mdx) - Install the ToolHive operator diff --git a/versioned_docs/version-1.0/toolhive/guides-k8s/token-exchange-k8s.mdx b/versioned_docs/version-1.0/toolhive/guides-k8s/token-exchange-k8s.mdx new file mode 100644 index 00000000..1befe9ff --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-k8s/token-exchange-k8s.mdx @@ -0,0 +1,266 @@ +--- +title: Configure token exchange for backend authentication +description: + How to set up token exchange so MCP servers can authenticate to backend + services in Kubernetes using the ToolHive Operator. +--- + +This guide shows you how to configure token exchange in Kubernetes, which allows +MCP servers to authenticate to backend APIs using short-lived, properly scoped +tokens instead of embedded secrets. + +For conceptual background on how token exchange works, see +[Backend authentication](../concepts/backend-auth.mdx). For CLI-based setup, see +[Configure token exchange](../guides-cli/token-exchange.mdx). + +## Prerequisites + +Before you begin, make sure you have: + +- Kubernetes cluster with RBAC enabled +- ToolHive Operator installed (see + [Deploy the ToolHive Operator](./deploy-operator.mdx)) +- `kubectl` access to your cluster +- An identity provider that supports + [RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) token exchange (such + as Okta, Auth0, or Keycloak) +- A backend service configured to accept tokens from your identity provider +- Familiarity with + [Authentication and authorization in Kubernetes](./auth-k8s.mdx) + +## Configure your identity provider + +Token exchange requires your identity provider to issue tokens for the backend +service when presented with a valid MCP server token. This involves: + +- Registering a token exchange client with credentials +- Defining audience and scopes for the backend service +- Creating access policies that permit token exchange + +For detailed IdP configuration steps, see +[Configure your identity provider](../guides-cli/token-exchange.mdx#configure-your-identity-provider) +in the CLI guide. + +## Create the token exchange configuration + +### Step 1: Create a Secret for client credentials + +Store the OAuth client secret that ToolHive uses to authenticate when performing +token exchange: + +```yaml title="token-exchange-secret.yaml" +apiVersion: v1 +kind: Secret +metadata: + name: token-exchange-secret + namespace: toolhive-system +type: Opaque +stringData: + client-secret: '' +``` + +```bash +kubectl apply -f token-exchange-secret.yaml +``` + +### Step 2: Create the MCPExternalAuthConfig resource + +Create an `MCPExternalAuthConfig` resource that defines the token exchange +parameters: + +```yaml title="token-exchange-config.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPExternalAuthConfig +metadata: + name: backend-token-exchange + namespace: toolhive-system +spec: + type: tokenExchange + tokenExchange: + tokenUrl: '' + audience: '' + clientId: '' + clientSecretRef: + name: token-exchange-secret + key: client-secret + scopes: + - '' +``` + +```bash +kubectl apply -f token-exchange-config.yaml +``` + +### Configuration reference + +| Field | Description | +| ----------------- | -------------------------------------------------------------- | +| `tokenUrl` | Your identity provider's token exchange endpoint | +| `audience` | Target audience for the exchanged token (your backend service) | +| `clientId` | Client ID for ToolHive to authenticate to the IdP | +| `clientSecretRef` | Reference to the Secret containing the client secret | +| `scopes` | Scopes to request for the backend service | + +## MCP server requirements + +The MCP server that ToolHive fronts must accept a per-request authentication +token. Specifically, the server should: + +- Read the access token from the `Authorization: Bearer` header +- Use this token to authenticate to the backend service +- Not rely on hardcoded secrets or environment variables for backend + authentication + +ToolHive injects the exchanged token into each request, so the MCP server +receives a fresh, properly scoped token for every call. + +## Deploy an MCP server with token exchange + +Create an `MCPServer` resource that references the token exchange configuration: + +```yaml title="mcpserver-token-exchange.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: my-mcp-server + namespace: toolhive-system +spec: + image: + transport: streamable-http + proxyPort: 8080 + # Reference the token exchange configuration + externalAuthConfigRef: + name: backend-token-exchange + # OIDC configuration for validating incoming client tokens + oidcConfig: + type: inline + inline: + issuer: '' + audience: '' + jwksUrl: '' +``` + +```bash +kubectl apply -f mcpserver-token-exchange.yaml +``` + +The `externalAuthConfigRef` tells ToolHive to use the token exchange +configuration you created earlier. The `oidcConfig` validates incoming client +tokens before performing the exchange. + +## Verify the configuration + +To confirm token exchange is working: + +1. Check the MCPServer status: + + ```bash + kubectl get mcpserver -n toolhive-system my-mcp-server + ``` + +2. Optionally, expose the server outside the cluster using an Ingress or Gateway + +3. Connect to the MCP server with a client that supports authentication + +4. Make a tool call that requires backend access + +5. Check the proxy logs for successful token exchange: + + ```bash + kubectl logs -n toolhive-system -l app.kubernetes.io/name=my-mcp-server + ``` + +You can also verify by examining your identity provider's logs for successful +token exchange requests, or by checking audit logs on your backend service to +confirm requests arrive with the correct user identity and scopes. + +## Example: Okta configuration + +This example shows a complete configuration using Okta for token exchange. + +### Secret + +```yaml title="okta-secret.yaml" +apiVersion: v1 +kind: Secret +metadata: + name: okta-token-exchange-secret + namespace: toolhive-system +type: Opaque +stringData: + client-secret: 'your-okta-client-secret' +``` + +### MCPExternalAuthConfig + +```yaml title="okta-token-exchange.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPExternalAuthConfig +metadata: + name: okta-backend-exchange + namespace: toolhive-system +spec: + type: tokenExchange + tokenExchange: + tokenUrl: 'https://dev-123456.okta.com/oauth2/aus9876543210/v1/token' + audience: 'backend-api' + clientId: '0oa0987654321fedcba' + clientSecretRef: + name: okta-token-exchange-secret + key: client-secret + scopes: + - 'api:read' + - 'api:write' +``` + +### MCPServer + +```yaml title="mcpserver-okta.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: my-backend-server + namespace: toolhive-system +spec: + image: your-mcp-server:latest + transport: streamable-http + proxyPort: 8080 + externalAuthConfigRef: + name: okta-backend-exchange + oidcConfig: + type: inline + # Set resourceUrl to the external URL if exposing outside the cluster + resourceUrl: 'https://my-backend-server.example.com' + inline: + issuer: 'https://dev-123456.okta.com/oauth2/aus1234567890' + audience: 'mcp-server' + jwksUrl: 'https://dev-123456.okta.com/oauth2/aus1234567890/v1/keys' +``` + +Key points in this example: + +- **Two authorization servers**: The `issuer` in `oidcConfig` (`aus1234567890`) + validates incoming client tokens. The `tokenUrl` in `MCPExternalAuthConfig` + uses a different authorization server (`aus9876543210`) that issues tokens for + the backend API. +- **Audience transformation**: Client tokens arrive with audience `mcp-server`. + ToolHive exchanges them for tokens with audience `backend-api`, which the + backend service expects. +- **Scope transformation**: The original token has MCP-specific scopes, while + the exchanged token has backend-specific scopes (`api:read`, `api:write`). The + user's identity is preserved, but the permissions are transformed for the + target service. + +## Related information + +- [Backend authentication](../concepts/backend-auth.mdx) - conceptual overview + of token exchange and federation +- [Configure token exchange (CLI)](../guides-cli/token-exchange.mdx) - CLI-based + setup +- [Authentication and authorization](./auth-k8s.mdx) - basic auth setup for MCP + servers in Kubernetes +- [CRD specification](../reference/crd-spec.md) - complete CRD reference + including MCPExternalAuthConfig +- [AWS STS integration](../integrations/aws-sts.mdx) - for AWS services, + ToolHive has built-in STS support using `MCPExternalAuthConfig` with + `type: awsSts` diff --git a/versioned_docs/version-1.0/toolhive/guides-mcp/_template.mdx b/versioned_docs/version-1.0/toolhive/guides-mcp/_template.mdx new file mode 100644 index 00000000..cbf8ba8e --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-mcp/_template.mdx @@ -0,0 +1,60 @@ +--- +title: SERVER_NAME MCP server guide +sidebar_label: SERVER_NAME +description: Using the SERVER_NAME MCP server with ToolHive for PURPOSE. +last_update: + author: YOUR_GITHUB_USERNAME + date: YYYY-MM-DD +--- + +## Overview + +A brief overview of the MCP server, its purpose, and key features. Link to the +official documentation. + +## Metadata + + + +## Usage + + + + +UI instructions go here. Only include a screenshot if it adds value, such as +showing a unique configuration option or feature. + + + + +CLI instructions go here, with multiple usage examples specific to the MCP +server. + +```bash +thv run ... +``` + +If appropriate, include guidance for using network isolation and/or a custom +permission profile. + + + + +Kubernetes manifest and instructions go here + +```yaml title="SERVER_NAME.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +``` + + + + +## Sample prompts + +Provide sample prompts that users can use to interact with the MCP server. + +## Recommended practices + +- Include some recommended practices for using the MCP server safely and + effectively diff --git a/versioned_docs/version-1.0/toolhive/guides-mcp/context7.mdx b/versioned_docs/version-1.0/toolhive/guides-mcp/context7.mdx new file mode 100644 index 00000000..c4139cf1 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-mcp/context7.mdx @@ -0,0 +1,179 @@ +--- +title: Context7 MCP server guide +sidebar_label: Context7 +description: Using the Context7 MCP server with ToolHive for up-to-date documentation. +last_update: + author: danbarr + date: 2026-02-10 +--- + +## Overview + +The [Context7 MCP server](https://github.com/upstash/context7) provides +up-to-date, version-specific documentation and code examples straight from the +source for any library or framework. Instead of relying on outdated training +data, Context7 fetches current documentation and API references directly into +your LLM's context, ensuring you get working code examples and accurate +information. + +Context7 eliminates common issues like hallucinated APIs that don't exist, +outdated code patterns, and generic answers based on old package versions. It +supports thousands of popular libraries including Next.js, React, MongoDB, +Supabase, and many more. + +Learn more at [context7.com](https://context7.com) and view the +[project documentation](https://github.com/upstash/context7) for additional +details. + +## Metadata + + + +## Usage + +While Context7 works without an API key, registering at +[context7.com/dashboard](https://context7.com/dashboard) provides: + +- Higher rate limits +- Priority access during peak usage +- Better performance for frequent queries + + + + +**Remote MCP server** + +If you have a Context7 account, you can select the `context7-remote` MCP server +to connect to the hosted service with dynamic OAuth authentication. Your browser +will open to authorize the server to access your Context7 account. + +**Local MCP server** + +Select the `context7` MCP server in the ToolHive registry. + +The server works without authentication for basic usage, but you can optionally +add an API key in the **Secrets** section for higher rate limits. + +:::tip[Security tip] + +Enable outbound network filtering on the **Network Isolation** tab to restrict +the server's network access using the default profile contained in the registry. + +::: + + + + +If you have a Context7 account, you can run the remote MCP server with dynamic +OAuth authentication. Your browser will open to authorize the server to access +your Context7 account. + +```bash +thv run context7-remote +``` + +Alternatively, run the local containerized MCP server with the default +configuration (you don't need an API key for basic usage): + +```bash +thv run context7 +``` + +Enable [network isolation](../guides-cli/network-isolation.mdx) using the +default profile from the registry to restrict the server's network access: + +```bash +thv run --isolate-network context7 +``` + +If you have a Context7 API key for higher rate limits, create a secret named +`context7` containing your API key and run the server with the `--secret` flag: + +```bash +thv secret set context7 +thv run --secret context7,target=CONTEXT7_API_KEY context7 +``` + +Combine API key with network isolation: + +```bash +thv run --secret context7,target=CONTEXT7_API_KEY --isolate-network context7 +``` + + + + +For basic usage without authentication: + +```yaml title="context7.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: context7 + namespace: toolhive-system +spec: + image: ghcr.io/stacklok/dockyard/npx/context7:2.1.1 + transport: stdio + proxyPort: 8080 +``` + +Apply the manifest to your cluster: + +```bash +kubectl apply -f context7.yaml +``` + +If you have a Context7 API key for higher rate limits, create a Kubernetes +secret containing your key: + +```bash +kubectl -n toolhive-system create secret generic context7-api-key --from-literal=token= +``` + +Then add a `secrets` section to the manifest file above to pass the API key as +an environment variable: + +```yaml title="context7-with-auth.yaml" +spec: + # ... + secrets: + - name: context7-api-key + key: token + targetEnvName: CONTEXT7_API_KEY +``` + + + + +## Sample prompts + +Here are practical prompts you can use with the Context7 MCP server: + +- "Create a Next.js middleware that checks for a valid JWT in cookies and + redirects unauthenticated users to `/login`. Use context7" +- "Show me how to set up MongoDB connection pooling with the latest MongoDB + Node.js driver. Use context7" +- "Configure a Supabase client with TypeScript types for a user authentication + system. Use context7" +- "Create a React component using the latest Tailwind CSS utility classes for a + responsive navigation bar. Use context7" +- "Show me how to implement server-side rendering with the current version of + Nuxt.js. Use context7" +- "Configure Redis caching with the Upstash Redis SDK for serverless functions. + Use context7" + +## Recommended practices + +- Include `use context7` in your prompts to automatically fetch current + documentation for the libraries you're working with. +- When you know the exact library, specify it directly using the Context7 ID + format, for example: `use library /supabase/supabase for api and docs` +- Consider setting up a rule in your MCP client to automatically invoke Context7 + for code-related queries, eliminating the need to manually add `use context7` + to each prompt. +- Use the `topic` parameter when requesting documentation to focus on specific + areas like "routing", "authentication", or "deployment". +- Register for an API key at + [context7.com/dashboard](https://context7.com/dashboard) if you plan to make + frequent requests or need higher rate limits. +- Enable network isolation to restrict the server's outbound access. diff --git a/versioned_docs/version-1.0/toolhive/guides-mcp/fetch.mdx b/versioned_docs/version-1.0/toolhive/guides-mcp/fetch.mdx new file mode 100644 index 00000000..b9dc2226 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-mcp/fetch.mdx @@ -0,0 +1,111 @@ +--- +title: Fetch MCP server guide +sidebar_label: Fetch +description: Using the Fetch MCP server with ToolHive to retrieve website data. +last_update: + author: danbarr + date: 2026-02-10 +--- + +## Overview + +A simple, lightweight MCP server that retrieves data from a website to add +real-time context to an AI agent workflow. + +[GoFetch](https://github.com/StacklokLabs/gofetch) is a Go implementation of the +original +[Fetch MCP server](https://github.com/modelcontextprotocol/servers/tree/main/src/fetch) +with improved performance and security, built by Stacklok. + +## Metadata + + + +## Usage + + + + +Select the `fetch` MCP server in the ToolHive registry. No additional +configuration is required to run it. + +By default, it can access any website. To restrict its network access, +[enable **network isolation**](../guides-ui/network-isolation.mdx) and enter the +allowed hosts and ports. + + + + +Run with the default configuration: + +```bash +thv run fetch +``` + +To control which website resources the server can access, create a custom +permission profile: + +```json title="fetch-profile.json" +{ + "network": { + "outbound": { + "insecure_allow_all": false, + "allow_host": [ + "host.docker.internal", + "intranet.example.com", + ".googleapis.com" + ], + "allow_port": [443] + } + } +} +``` + +Then run the server with the profile and +[enable network isolation](../guides-cli/network-isolation.mdx): + +```bash +thv run --isolate-network --permission-profile fetch-profile.json fetch +``` + + + + +Create a Kubernetes manifest to deploy the Fetch MCP server: + +```yaml title="fetch.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: fetch + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/gofetch/server:1.0.3 + transport: streamable-http + mcpPort: 8080 + proxyPort: 8080 +``` + +Apply the manifest to your Kubernetes cluster: + +```bash +kubectl apply -f fetch.yaml +``` + + + + +## Sample prompts + +Here are some sample prompts you can use to interact with the Fetch MCP server: + +- "Fetch the latest news from `https://news.ycombinator.com`" +- "Get the current weather for `https://weather.com`" +- "Retrieve the latest blog posts from `https://example.com/blog`" + +## Recommended practices + +- Use network isolation to restrict the server's outbound network access to the + specific hosts and ports required for your use case. +- Enable [telemetry](../guides-cli/telemetry-and-metrics.mdx) to monitor tool + usage including URL access for security and auditing purposes. diff --git a/versioned_docs/version-1.0/toolhive/guides-mcp/filesystem.mdx b/versioned_docs/version-1.0/toolhive/guides-mcp/filesystem.mdx new file mode 100644 index 00000000..f739a60d --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-mcp/filesystem.mdx @@ -0,0 +1,175 @@ +--- +title: Filesystem MCP server guide +sidebar_label: Filesystem +description: Using the Filesystem MCP server with ToolHive for file access. +last_update: + author: danbarr + date: 2026-02-10 +--- + +import useBaseUrl from '@docusaurus/useBaseUrl'; +import ThemedImage from '@theme/ThemedImage'; + +## Overview + +The +[Filesystem MCP server](https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem) +provides access to the local filesystem, allowing AI agents to read and write +files as part of their workflows. + +:::note + +Since most AI agent host applications like IDEs already have access to your +working directory, this MCP server is primarily useful for access to files +outside your working directory, headless environments where the host application +does not provide filesystem access, or for demonstrating MCP capabilities. + +::: + +## Metadata + + + +## Usage + + + + +Select the `filesystem` MCP server in the ToolHive registry. + +In the **Storage volumes** section, +[add local files or folders](../guides-ui/run-mcp-servers.mdx#volumes) to expose +to the MCP server. In the drop-down, choose whether to mount the volume as +read-only or read-write. + +:::note + +By default, the server expects files to be located in `/projects`. If you use a +different container path, you must update the command arguments to replace +`/projects` with your custom path. + +::: + + + +:::tip[Security tip] + +Since the server does not require any network access, +[enable **network isolation**](../guides-ui/network-isolation.mdx) and do not +add any hosts or ports to completely restrict its outbound network access. + +::: + + + + +[Mount a directory](../guides-cli/filesystem-access.mdx) from the host +filesystem to the MCP server using the default container path: + +```bash +thv run --volume /path/to/host/directory:/projects filesystem +``` + +:::note + +By default, the server expects files to be located in `/projects`. If you use a +different container path, you must update the command arguments to replace +`/projects` with your custom path. + +::: + +Mount multiple files or directories by repeating the `--volume` flag. This +example mounts a directory under `/projects` and a file under `/data`, and +updates the command arguments accordingly: + +```bash +thv run \ + --volume /path/to/host/directory1:/projects/dir1 \ + --volume /path/to/host/file.txt:/data/file.txt:ro \ + filesystem -- /projects /data +``` + +:::tip + +Since the server does not require any network access, add the +`--isolate-network --permission-profile none` flags to completely restrict its +outbound network access (see the +[network isolation guide](../guides-cli/network-isolation.mdx)). + +::: + + + + +Create a Kubernetes manifest to deploy the Filesystem MCP server with a +[persistent volume](../guides-k8s/run-mcp-k8s.mdx#mount-a-volume). + +Update the `podTemplateSpec` section to include your specific volume claim and +mount path: + +```yaml {14-17,20-23} title="filesystem.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: filesystem + namespace: toolhive-system +spec: + image: mcp/filesystem:1.0.2 + transport: stdio + proxyPort: 8080 + args: + - '/projects' # Update if you use a different mountPath below + podTemplateSpec: + spec: + volumes: + - name: my-mcp-data + persistentVolumeClaim: + claimName: my-mcp-data-claim + containers: + - name: mcp + volumeMounts: + - mountPath: /projects/my-mcp-data + name: my-mcp-data + readOnly: true +``` + +:::note + +If you change the mount path from `/projects`, you must also update the `args` +section to include the path. + +::: + +Apply the manifest to your Kubernetes cluster: + +```bash +kubectl apply -f filesystem.yaml +``` + + + + +## Sample prompts + +Here are some sample prompts you can use to interact with the Filesystem MCP +server: + +- "List all files in the `/projects` directory" +- "Read the contents of the file `/projects/example.txt`" +- "Write 'Hello, World!' to the file `/projects/hello.txt`" + +## Recommended practices + +- Mount only the directories or files required for your AI agent's tasks to + minimize resource usage and improve performance. +- Use read-only mounts for directories or files that do not need to be modified + by the AI agent to prevent accidental changes. +- Enable network isolation to restrict the server's outbound network access, + since the filesystem MCP server does not require any network connectivity. diff --git a/versioned_docs/version-1.0/toolhive/guides-mcp/github.mdx b/versioned_docs/version-1.0/toolhive/guides-mcp/github.mdx new file mode 100644 index 00000000..fff3e1e0 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-mcp/github.mdx @@ -0,0 +1,211 @@ +--- +title: GitHub MCP server guide +sidebar_label: GitHub +description: Using the GitHub MCP server with ToolHive for repository management. +last_update: + author: danbarr + date: 2026-02-10 +--- + +## Overview + +The official [GitHub MCP server](https://github.com/github/github-mcp-server) +provides access to the GitHub API, allowing AI agents to interact with GitHub +repositories, issues, pull requests, and more. + +## Metadata + + + +## Usage + + + + +Select the `github` MCP server in the ToolHive registry. In the **Secrets** +section, add your GitHub personal access token to authenticate with the GitHub +API, or select an existing secret that contains the token. + +Review the optional environment variables to customize the server's behavior. +For example, you might want to limit the active toolsets or enable read-only +mode. Refer to the +[documentation](https://github.com/github/github-mcp-server?tab=readme-ov-file#tool-configuration) +for the current list of toolsets. + +:::tip[Security tip] + +Enable outbound network filtering on the **Network Isolation** tab to restrict +the server's network access using the default profile contained in the registry. + +::: + +:::info[GitHub Enterprise] + +If you're working with a GitHub Enterprise instance, enter the instance URL in +the `GITHUB_HOST` environment variable and update the network isolation settings +to allow access to the enterprise domain. + +::: + + + + +Run with the default configuration. ToolHive will prompt you to enter your +GitHub personal access token: + +```bash +thv run github +``` + +Create a secret named `github` containing your GitHub personal access token and +run the server with the `--secret` flag: + +```bash +thv secret set github +thv run --secret github,target=GITHUB_PERSONAL_ACCESS_TOKEN github +``` + +Or, use the GitHub CLI to populate the secret with your token: + +```bash +gh auth token | thv secret set github +thv run --secret github,target=GITHUB_PERSONAL_ACCESS_TOKEN github +``` + +Enable [network isolation](../guides-cli/network-isolation.mdx) using the +default profile from the registry (appropriate for `github.com`) to restrict the +server's network access: + +```bash +thv run --isolate-network github +``` + +Limit the active toolsets (useful to avoid context overload) and enable +read-only mode. Refer to the +[documentation](https://github.com/github/github-mcp-server?tab=readme-ov-file#tool-configuration) +for the current list of toolsets. + +```bash +thv run -e GITHUB_TOOLSETS=repos,issues,pull_requests -e GITHUB_READ_ONLY=1 github +``` + +Enable the MCP server's dynamic tool discovery feature (currently in beta): + +```bash +thv run -e GITHUB_DYNAMIC_TOOLSETS=1 github +``` + +:::info[GitHub Enterprise] + +Create a custom permission profile for your GitHub Enterprise instance: + +```json title="github-enterprise-profile.json" +{ + "network": { + "outbound": { + "insecure_allow_all": false, + "allow_host": ["github.your-enterprise.com"], + "allow_port": [443] + } + } +} +``` + +Then run the server with the profile: + +```bash +thv run \ + -e GITHUB_HOST=https://github.your-enterprise.com \ + --isolate-network --permission-profile github-enterprise-profile.json \ + github +``` + +::: + + + + +Create a Kubernetes secret containing your GitHub personal access token: + +```bash +kubectl -n toolhive-system create secret generic github-token --from-literal=token= +``` + +Create a Kubernetes manifest to deploy the GitHub MCP server using your secret: + +```yaml {10-14} title="github.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: github + namespace: toolhive-system +spec: + image: ghcr.io/github/github-mcp-server:v0.31.0 + transport: stdio + proxyPort: 8080 + secrets: + - name: github-token + key: token + targetEnvName: GITHUB_PERSONAL_ACCESS_TOKEN +``` + +Apply the manifest to your Kubernetes cluster: + +```bash +kubectl apply -f github.yaml +``` + +To customize the server's behavior, add environment variables to the `spec` +section of your manifest. For example, to limit the active toolsets or enable +read-only mode, add: + +```yaml title="github.yaml" +spec: + # ... + env: + - name: GITHUB_TOOLSETS + value: 'repos,issues,pull_requests' + - name: GITHUB_READ_ONLY + value: '1' +``` + +Refer to the +[documentation](https://github.com/github/github-mcp-server?tab=readme-ov-file#tool-configuration) +for the current list of toolsets. + +:::info[GitHub Enterprise] + +If you're working with a GitHub Enterprise instance, add the `GITHUB_HOST` +environment variable to the `spec` section of your manifest: + +```yaml +spec: + # ... + env: + - name: GITHUB_HOST + value: 'https://github.your-enterprise.com' +``` + +::: + + + + +## Sample prompts + +Here are some sample prompts you can use to interact with the GitHub MCP server: + +- "List all repositories for the organization `my-org`" +- "Create a new issue in the repository `my-org/my-repo` with the title 'Bug + report' and the body 'There is a bug in the code'" +- "Get the latest pull requests for the repository `my-org/my-repo`" + +## Recommended practices + +- Scope your GitHub personal access token to the minimum permissions required + for your use case. +- Regularly rotate your GitHub personal access token and update the secret in + ToolHive. +- Enable network isolation to restrict the server's outbound network access. +- Limit the active toolsets to reduce context overload and improve performance, + or use dynamic tool discovery if supported by your client. diff --git a/versioned_docs/version-1.0/toolhive/guides-mcp/grafana.mdx b/versioned_docs/version-1.0/toolhive/guides-mcp/grafana.mdx new file mode 100644 index 00000000..5b6066e2 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-mcp/grafana.mdx @@ -0,0 +1,234 @@ +--- +title: Grafana MCP server guide +sidebar_label: Grafana +description: + Using the Grafana MCP server with ToolHive for dashboard management, + datasource queries, alerting, and incident response. +last_update: + author: danbarr + date: 2026-02-10 +--- + +## Overview + +The official [Grafana MCP server](https://github.com/grafana/mcp-grafana) +provides comprehensive access to your Grafana instance and its surrounding +ecosystem. With support for dashboards, datasource queries (Prometheus, Loki, +Pyroscope), alerting, incident management, Grafana OnCall, and Sift +investigations, this server enables AI agents to interact with your entire +observability stack. + +The server works with both local Grafana instances and Grafana Cloud, making it +ideal for tasks like troubleshooting production issues, analyzing metrics and +logs, managing dashboards, and coordinating incident response. + +## Metadata + + + +## Usage + +You'll need a Grafana service account token to authenticate with the Grafana +API. The token must have permissions for the Grafana features you want to access +(such as dashboards, datasources, or alerting). Refer to the +[Grafana service account documentation](https://grafana.com/docs/grafana/latest/administration/service-accounts/) +for details on creating tokens and configuring permissions. + + + + +Select the `grafana` MCP server in the ToolHive registry. + +In the **Secrets** section, add your Grafana service account token or select an +existing secret that contains the token. + +In the **Environment Variables** section, configure the connection to your +Grafana instance: + +- `GRAFANA_URL`: Your Grafana instance URL (for example, `http://localhost:3000` + for local instances or `https://myinstance.grafana.net` for Grafana Cloud) +- `GRAFANA_ORG_ID` (optional): The numeric organization ID if your Grafana + instance has multiple organizations + +:::tip[Security tip] + +Enable outbound network filtering on the **Network Isolation** tab to restrict +the server's network access. Update the allowed hosts to match your Grafana +instance domain. + +::: + + + + +Create a secret containing your Grafana service account token: + +```bash +thv secret set grafana-token +``` + +Run the server with your Grafana instance URL and the secret: + +```bash +thv run \ + -e GRAFANA_URL=http://localhost:3000 \ + --secret grafana-token,target=GRAFANA_SERVICE_ACCOUNT_TOKEN \ + grafana +``` + +For Grafana Cloud, use your cloud instance URL: + +```bash +thv run \ + -e GRAFANA_URL=https://myinstance.grafana.net \ + --secret grafana-token,target=GRAFANA_SERVICE_ACCOUNT_TOKEN \ + grafana +``` + +Enable [network isolation](../guides-cli/network-isolation.mdx) to restrict the +server's network access. Create a permission profile with your Grafana instance +domain: + +```json title="grafana-profile.json" +{ + "network": { + "outbound": { + "insecure_allow_all": false, + "allow_host": ["myinstance.grafana.net"], + "allow_port": [443] + } + } +} +``` + +Then run with the custom profile: + +```bash +thv run \ + -e GRAFANA_URL=https://myinstance.grafana.net \ + --secret grafana-token,target=GRAFANA_SERVICE_ACCOUNT_TOKEN \ + --isolate-network --permission-profile grafana-profile.json \ + grafana +``` + +If your Grafana instance has multiple organizations, add the `GRAFANA_ORG_ID` +environment variable with the numeric organization ID (for example, +`-e GRAFANA_ORG_ID=2`). + +:::tip[Debug mode] + +Add the `--` separator followed by `-debug` to enable detailed logging of HTTP +requests and responses: + +```bash +thv run \ + -e GRAFANA_URL=http://localhost:3000 \ + --secret grafana-token,target=GRAFANA_SERVICE_ACCOUNT_TOKEN \ + -- -debug +``` + +::: + + + + +Create a Kubernetes secret containing your Grafana service account token: + +```bash +kubectl -n toolhive-system create secret generic grafana-token \ + --from-literal=token= +``` + +Create a Kubernetes manifest to deploy the Grafana MCP server, specifying your +Grafana instance URL and referencing the secret. This example uses a Grafana +instance running in an `observability` namespace in the cluster: + +```yaml title="grafana.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: grafana + namespace: toolhive-system +spec: + image: docker.io/grafana/mcp-grafana:0.11.2 + transport: streamable-http + mcpPort: 8000 + proxyPort: 8080 + args: + - --transport + - streamable-http + env: + - name: GRAFANA_URL + value: 'http://grafana.observability.svc.cluster.local:3000' + secrets: + - name: grafana-token + key: token + targetEnvName: GRAFANA_SERVICE_ACCOUNT_TOKEN +``` + +Apply the manifest to your Kubernetes cluster: + +```bash +kubectl apply -f grafana.yaml +``` + +For Grafana Cloud, update the `GRAFANA_URL` in the manifest: + +```yaml +spec: + env: + - name: GRAFANA_URL + value: 'https://myinstance.grafana.net' +``` + +If your Grafana instance has multiple organizations, add the `GRAFANA_ORG_ID` +environment variable with the numeric organization ID: + +```yaml +spec: + env: + - name: GRAFANA_URL + value: 'http://grafana.observability.svc.cluster.local:3000' + - name: GRAFANA_ORG_ID + value: '2' +``` + + + + +## Sample prompts + +Here are some sample prompts you can use to interact with the Grafana MCP +server: + +- "Show me all dashboards related to Kubernetes monitoring" +- "Query the Prometheus datasource for CPU usage over the last hour for the + `api-service` pod" +- "Get the recent alerts that are currently firing" +- "List all open incidents and show me details for the most recent one" +- "Find error patterns in the logs from the `production` namespace using Loki" +- "Who is currently on call for the backend team schedule?" +- "Create a new incident titled 'High memory usage on production cluster' with + severity critical" +- "Show me the panel queries from the 'API Performance' dashboard" +- "Get label values for the `namespace` label from the Loki datasource" +- "List all Sift investigations from the past week" + +## Recommended practices + +- Create service accounts with least-privilege permissions. Use fine-grained + RBAC scopes to limit access to only the datasources, dashboards, and features + required for your specific use case. +- Regularly rotate service account tokens and update the secrets in ToolHive. +- Enable network isolation to restrict the server's outbound network access to + your Grafana instance domain only. +- For dashboards with large JSON configurations, use the `get_dashboard_summary` + or `get_dashboard_property` tools to minimize context window usage instead of + retrieving the full dashboard with `get_dashboard_by_uid`. +- When working with multi-organization setups, always specify the + `GRAFANA_ORG_ID` to ensure operations target the correct organization. +- Enable [telemetry](../guides-cli/telemetry-and-metrics.mdx) to monitor API + calls and track which Grafana resources are being accessed. +- For production deployments, consider using the debug mode temporarily to + troubleshoot connection or permission issues, but disable it once everything + is working correctly. diff --git a/versioned_docs/version-1.0/toolhive/guides-mcp/k8s.mdx b/versioned_docs/version-1.0/toolhive/guides-mcp/k8s.mdx new file mode 100644 index 00000000..b2d2ee46 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-mcp/k8s.mdx @@ -0,0 +1,225 @@ +--- +title: Kubernetes MCP server guide +sidebar_label: Kubernetes (MKP) +description: Using the Kubernetes (MKP) MCP server with ToolHive for cluster management. +last_update: + author: danbarr + date: 2026-02-10 +--- + +## Overview + +An MCP server that interacts with Kubernetes clusters, enabling you to manage +and automate tasks within your Kubernetes environment. + +This server is based on the +[MKP (Model Kontext Protocol)](https://github.com/StacklokLabs/mkp) project, +which is a native Go implementation of an MCP server for Kubernetes that +directly interacts with the Kubernetes API. + +## Metadata + + + +## Usage + + + + +Select the `k8s` MCP server in the ToolHive registry. The server requires +Kubernetes cluster access, so you'll need to configure authentication. + +In the **Volumes** section, mount your kubeconfig file to provide cluster +access. The most common setup is: + +- **Host path**: `/home/$USER/.kube` (your local kubeconfig directory) +- **Container path**: `/home/nonroot/.kube` +- **Read only access** (recommended for security) + +Alternatively, if you have a specific kubeconfig file, mount it directly: + +- **Host path**: `/path/to/your/kubeconfig` +- **Container path**: `/home/nonroot/.kube/config` +- **Read only access** + +:::note[Write Operations] + +By default, the server runs in read-only mode. To enable write operations (like +applying resources), add the `--read-write=true` argument in the **Command +arguments** section. Use with caution in production environments. + +::: + +:::tip[Security tip] + +Enable outbound network filtering on the **Network Isolation** tab to restrict +the server's network access to your Kubernetes cluster endpoints only. Add your +cluster's API server host and port (usually 443 or 6443) to the allowed list. + +::: + + + + +Run with the default configuration, mounting your local `.kube` directory as +read-only: + +```bash +thv run --volume $HOME/.kube:/home/nonroot/.kube:ro k8s +``` + +Mount a specific kubeconfig file: + +```bash +thv run --volume /path/to/kubeconfig:/home/nonroot/.kube/config:ro k8s +``` + +Enable write operations (create/update/delete resources) with the `--read-write` +flag: + +```bash +thv run --volume $HOME/.kube:/home/nonroot/.kube:ro \ + k8s -- --read-write=true +``` + +Disable resource discovery to reduce context size in large clusters: + +```bash +thv run --volume $HOME/.kube:/home/nonroot/.kube:ro \ + --arg --serve-resources=false \ + k8s +``` + +Enable [network isolation](../guides-cli/network-isolation.mdx) to restrict +network access to your Kubernetes cluster only. Create a custom permission +profile: + +```json title="k8s-cluster-profile.json" +{ + "network": { + "outbound": { + "insecure_allow_all": false, + "allow_host": ["your-cluster-endpoint.com", "kubernetes.default.svc"], + "allow_port": [443, 6443] + } + } +} +``` + +Then run with network isolation: + +```bash +thv run --volume $HOME/.kube:/home/nonroot/.kube:ro \ + --isolate-network --permission-profile k8s-cluster-profile.json \ + k8s +``` + + + + +This example follows the recommended practice of running the MKP MCP server +inside the cluster it's managing. A service account is used for authentication. + +Create a Kubernetes manifest to deploy the MKP server. This example creates a +service account with `cluster-admin` permissions for simplicity. In production, +create a custom ClusterRole with only the minimum permissions required. + +```yaml title="mkp.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: mkp + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/mkp/server:0.2.4 + transport: streamable-http + mcpPort: 8080 + proxyPort: 8080 + serviceAccount: mkp-sa +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: mkp-sa + namespace: toolhive-system +--- +# NOTE: This ClusterRoleBinding uses cluster-admin for example purposes only. +# In production, you should create a custom ClusterRole with the minimum +# permissions required by your MCP server instead of using cluster-admin. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: mkp-sa-cluster-admin +subjects: + - kind: ServiceAccount + name: mkp-sa + namespace: toolhive-system +roleRef: + kind: ClusterRole + name: cluster-admin + apiGroup: rbac.authorization.k8s.io +``` + +Apply the manifest to your Kubernetes cluster: + +```bash +kubectl apply -f mkp.yaml +``` + +To customize the server's behavior, add CLI arguments to the `spec` section of +your manifest. For example, to enable write operations: + +```yaml title="mkp.yaml" +spec: + # ... + args: + - '--read-write=true' +``` + +:::info[Configure tools] + +To filter or rename the tools exposed by your MCP server on Kubernetes, use the +MCPToolConfig CRD and reference it from your MCPServer with the `toolConfigRef` +field. See +[Configure tools for MCP servers on Kubernetes](../guides-k8s/customize-tools.mdx). + +::: + + + + +## Sample prompts + +Here are some sample prompts you can use to interact with the Kubernetes MCP +server: + +- "List all pods in the default namespace" +- "Show me the status of all deployments across all namespaces" +- "Get the logs from the pod named `nginx-pod` in the default namespace" +- "Create a new deployment with 3 replicas of nginx in the production namespace" +- "Show me all services in the kube-system namespace" +- "List all nodes in the cluster and show their resource usage" +- "Get the YAML definition of the deployment named `web-app` in the staging + namespace" +- "Show me all persistent volume claims that are not bound" +- "Apply this ConfigMap to the development namespace" + +## Recommended practices + +- **Use read-only mode by default**: Only enable write operations + (`--read-write=true`) when necessary and in controlled environments. +- **Implement proper RBAC**: When using service accounts, follow the principle + of least privilege and grant only the minimum permissions required. +- **Enable network isolation**: Restrict network access to your Kubernetes API + endpoints only, especially in production environments. +- **Monitor resource usage**: The server includes built-in rate limiting, but + monitor your cluster's API server load when using with AI agents. +- **Disable resource discovery in large clusters**: Use + `--serve-resources=false` to reduce context size and improve performance in + clusters with many resources. +- **Secure kubeconfig files**: When mounting kubeconfig files, always use + read-only mounts and ensure proper file permissions. +- **Use namespace scoping**: When possible, limit the server's access to + specific namespaces rather than cluster-wide permissions. +- **Enable audit logging**: Configure Kubernetes audit logging to track API + calls made by the MCP server for security monitoring. diff --git a/versioned_docs/version-1.0/toolhive/guides-mcp/notion-remote.mdx b/versioned_docs/version-1.0/toolhive/guides-mcp/notion-remote.mdx new file mode 100644 index 00000000..62dd7b47 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-mcp/notion-remote.mdx @@ -0,0 +1,212 @@ +--- +title: Notion MCP server guide +sidebar_label: Notion +description: + Using the Notion remote MCP server with ToolHive to access your Notion + workspace. +last_update: + author: danbarr + date: 2026-02-10 +--- + +## Overview + +The official [Notion remote MCP server](https://developers.notion.com/docs/mcp) +provides access to your Notion workspace through the Notion API, allowing AI +agents to search, read, create, and manage pages, databases, and other content +in your Notion workspace. + +This is a remote MCP server that uses the **Auto-Discovered** authorization +method, meaning ToolHive handles OAuth authentication automatically with minimal +configuration required. + +## Metadata + + + +## Usage + + + + +Select the `notion-remote` MCP server in the ToolHive registry. + +The server is preconfigured with the following settings: + +- **Server URL**: `https://mcp.notion.com/mcp` +- **Transport**: Streamable HTTP +- **Authorization method**: Auto-Discovered + +In the **Callback port** field, enter the port that ToolHive should use to +listen for the OAuth callback. This can be any available port on your machine. + +When you install the server, ToolHive: + +1. Discovers the OAuth endpoints automatically +2. Registers a new OAuth client with Notion +3. Opens your browser for authentication + +After you authorize access in your browser, the remote MCP server appears in +your server list with a "Running" status. + + + + +Run the Notion remote MCP server with the default configuration: + +```bash +thv run notion-remote +``` + +When you run the server, ToolHive: + +1. Discovers the OAuth endpoints automatically +2. Registers a new OAuth client with Notion +3. Opens your browser for authentication + +Customize the OAuth callback port if the default port (`8777`) is unavailable: + +```bash +thv run --remote-auth-callback-port 50051 notion-remote +``` + +After successful authentication, the server will be available to your configured +MCP clients. + +To restart the server (which triggers a new OAuth authentication flow): + +```bash +thv restart notion-remote +``` + + + + +:::note + +The ToolHive Kubernetes operator does not currently support remote MCP servers +using dynamic OAuth authentication. Instead, you can run the +[local Notion MCP server](https://github.com/makenotion/notion-mcp-server) in +Kubernetes using a static integration key. + +::: + +Create an integration in Notion to obtain an authentication token by following +the instructions in the MCP server's +[README](https://github.com/makenotion/notion-mcp-server?tab=readme-ov-file#installation). + +Create a Kubernetes secret containing your Notion integration token +("`ntn_****`"): + +```bash +kubectl -n toolhive-system create secret generic notion-token --from-literal=token= +``` + +Create a Kubernetes manifest to deploy the Notion MCP server using your secret: + +```yaml title="notion.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: notion + namespace: toolhive-system +spec: + image: ghcr.io/stacklok/dockyard/npx/notion:2.1.0 + transport: stdio + port: 8080 + permissionProfile: + type: builtin + name: network + secrets: + - name: notion-token + key: token + targetEnvName: NOTION_TOKEN +``` + +Apply the manifest to your Kubernetes cluster: + +```bash +kubectl apply -f notion.yaml +``` + + + + +## Sample prompts + +Here are some sample prompts you can use to interact with the Notion MCP server: + +- "Search my Notion workspace for pages about 'project planning'" +- "Create a new page in my Notion workspace titled 'Meeting Notes' with today's + date" +- "Find all database entries in my Task List database where status is 'To Do'" +- "Update the page titled 'Weekly Report' with a summary of this week's + accomplishments" +- "List all pages in my 'Engineering' teamspace" +- "Create a new Notion database for tracking customer feedback with columns for + name, date, and feedback text" +- "Add a comment to the page titled 'Q4 Planning' with my feedback" +- "Search for pages created by john@example.com in the last 30 days" + +## Recommended practices + +- **Scope your access**: When authenticating, review the permissions requested + and only grant access to the workspaces you need for your use case. +- **Test with read operations**: Start with search and fetch operations to + familiarize yourself with the server's capabilities before making changes to + your workspace. +- **Use specific searches**: Instead of broad workspace searches, narrow down + results by searching within specific pages, databases, or teamspaces for + better performance. +- **Understand the data model**: Notion has a specific hierarchy (workspaces, + teamspaces, pages, databases, data sources). Understanding this structure + helps craft more effective prompts. +- **Be cautious with updates**: The MCP server can modify and delete content in + your Notion workspace. Always review changes before applying them to important + pages or databases. +- **Handle authentication errors**: If you see authentication errors, restart + the server to trigger a new OAuth flow: `thv restart notion-remote` + +## Troubleshooting + +
+OAuth authentication fails + +If OAuth authentication fails or times out: + +1. Ensure the callback port is not blocked by a firewall +2. Check that your browser allows pop-ups from ToolHive +3. Try restarting the server with `thv restart notion-remote` + +
+
+Server shows "Running" but tools don't work + +The server may have lost authentication. Restart the server to re-authenticate: + +```bash +thv restart notion-remote +``` + +
+
+Can't find content in search results + +- Notion search uses semantic search, which may return different results than + exact keyword matching +- Try using more specific search terms or filtering by page, database, or + teamspace +- Check that you have access to the content you're searching for (permissions + may limit results) + +
+
+Rate limits + +Notion's API has rate limits. If you encounter rate limit errors: + +- Reduce the frequency of requests +- Use more specific queries to reduce the amount of data retrieved +- Wait for the rate limit to reset (typically a few minutes) + +
diff --git a/versioned_docs/version-1.0/toolhive/guides-mcp/osv.mdx b/versioned_docs/version-1.0/toolhive/guides-mcp/osv.mdx new file mode 100644 index 00000000..50e65b7a --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-mcp/osv.mdx @@ -0,0 +1,121 @@ +--- +title: OSV MCP server guide +sidebar_label: Open Source Vulnerabilities (OSV) +description: Using the Open Source Vulnerabilities database (OSV) MCP server with ToolHive. +last_update: + author: danbarr + date: 2026-02-10 +--- + +## Overview + +The [OSV MCP server](https://github.com/StacklokLabs/osv-mcp) provides access to +the [Open Source Vulnerabilities database](https://osv.dev), which aggregates +vulnerability data from multiple sources including GitHub Security Advisories, +PyPA, RustSec, and many others. This server enables AI agents to: + +- Query vulnerabilities for specific package versions or Git commits +- Perform batch vulnerability queries across multiple packages +- Retrieve detailed vulnerability information by OSV ID +- Access comprehensive vulnerability data including severity, affected versions, + and remediation guidance + +The server supports various package ecosystems including npm, PyPI, Go modules, +Maven, NuGet, and more, making it an essential tool for security analysis and +dependency management workflows. + +## Metadata + + + +## Usage + + + + +Select the `osv` MCP server in the ToolHive registry. + +The OSV MCP server does not require any additional configuration or secrets. It +communicates directly with the public OSV API at `api.osv.dev`. + +:::tip[Security tip] + +Enable outbound network filtering on the **Network Isolation** tab to restrict +the server's network access using the default profile contained in the registry. + +::: + + + + +Run with the default configuration: + +```bash +thv run osv +``` + +Enable [network isolation](../guides-cli/network-isolation.mdx) using the +default profile from the registry to restrict the server's network access to +only the OSV API: + +```bash +thv run --isolate-network osv +``` + + + + +Create a Kubernetes manifest to deploy the OSV MCP server: + +```yaml title="osv.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: osv + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/osv-mcp/server:0.1.0 + transport: streamable-http + mcpPort: 8080 + proxyPort: 8080 +``` + +Apply the manifest to your Kubernetes cluster: + +```bash +kubectl apply -f osv.yaml +``` + + + + +## Sample prompts + +Here are some sample prompts you can use to interact with the OSV MCP server: + +- "Check if the npm package lodash version 4.17.15 has any known + vulnerabilities" +- "Query vulnerabilities for the Python package jinja2 version 2.4.1 and the Go + module github.com/gin-gonic/gin version 1.6.3" +- "Get detailed information about vulnerability GHSA-vqj2-4v8m-8vrq including + affected versions and remediation steps" +- "Check for vulnerabilities in commit hash + 6879efc2c1596d11a6a6ad296f80063b558d5e0f" +- "Scan these packages for vulnerabilities: express@4.17.1, react@16.13.0, and + django@3.0.5" +- "Look up vulnerability CVE-2021-44228 and provide details about affected + packages and severity" + +## Recommended practices + +- **Use batch queries** when checking multiple packages to reduce API calls and + improve performance. The batch query tool can handle multiple packages in a + single request. +- **Enable network isolation** to restrict the server's network access to only + the OSV API endpoints, improving security posture. +- **Specify ecosystems accurately** (npm, PyPI, Go, Maven, etc.) to ensure + accurate vulnerability matching and reduce false positives. +- **Use package URLs (PURLs)** when available for more precise package + identification across different ecosystems and registries. +- **Monitor rate limits** when performing large-scale vulnerability scans to + avoid overwhelming the OSV API service. diff --git a/versioned_docs/version-1.0/toolhive/guides-mcp/playwright.mdx b/versioned_docs/version-1.0/toolhive/guides-mcp/playwright.mdx new file mode 100644 index 00000000..2c27e147 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-mcp/playwright.mdx @@ -0,0 +1,273 @@ +--- +title: Playwright MCP server guide +sidebar_label: Playwright +description: Using the Playwright MCP server with ToolHive for browser automation. +last_update: + author: danbarr + date: 2026-02-10 +--- + +import useBaseUrl from '@docusaurus/useBaseUrl'; +import ThemedImage from '@theme/ThemedImage'; + +## Overview + +The official +[Playwright MCP server](https://github.com/microsoft/playwright-mcp) provides +browser automation capabilities using [Playwright](https://playwright.dev). This +server enables AI agents to interact with web pages through structured +accessibility snapshots, bypassing the need for screenshots or visually-tuned +models. + +Key features include: + +- **Fast and lightweight**: Uses Playwright's accessibility tree instead of + pixel-based input +- **LLM-friendly**: Operates purely on structured data without requiring vision + models +- **Deterministic tool application**: Avoids ambiguity common with + screenshot-based approaches +- **Multi-browser support**: Works with Chrome, Firefox, Safari (WebKit), and + Edge +- **Tab management**: Create, switch between, and manage multiple browser tabs +- **Persistent sessions**: Maintains login state and browser data between + interactions + +## Metadata + + + +## Usage + +The Playwright MCP server supports numerous command-line arguments to customize +its behavior. Common options include: + +- **Custom viewport**: Set `--viewport-size` to specify browser dimensions + (e.g., `1920,1080`) +- **Device emulation**: Use `--device` to emulate mobile devices (e.g., + `"iPhone 15"`) +- **Network isolation**: Configure `--allowed-origins` and `--blocked-origins` + to control which websites the browser can access +- **Output directory**: Specify `--output-dir` to save screenshots, PDFs, and + other files to a custom location; mount a host directory for persistence (see + examples below) + +Refer to the +[Playwright MCP server documentation](https://github.com/microsoft/playwright-mcp?tab=readme-ov-file#configuration) +for the full list of configuration options and their descriptions. + +:::note[Limitations] + +The containerized version of Playwright only supports the Chromium browser in +headless mode. + +::: + + + + +Select the `playwright` MCP server in the ToolHive registry. The server runs +with default settings that work for most use cases. + +To customize the server's behavior, add command-line arguments in the **Command +arguments** section. + +To save browser output files like screenshots and traces, mount a host directory +in the **Storage volumes** section and add the `--output-dir ` +command argument as shown in the screenshot below. + + + + + + +Run with the default configuration using Chromium in headless mode: + +```bash +thv run playwright +``` + +Emulate a mobile device for responsive testing: + +```bash {2} +thv run playwright -- --device "iPhone 15" +``` + +Restrict access to specific domains: + +```bash {2} +thv run playwright -- --allowed-origins "example.com;trusted-site.com" +``` + +Mount a host directory (e.g., `~/playwright-output`) to save browser output +files like screenshots and traces: + +```bash {3,5} +mkdir ~/playwright-output +thv run \ + --volume ~/playwright-output:/browser-output playwright \ + -- --output-dir /browser-output --save-trace --save-session +``` + +You can run multiple instances of the server with different configurations by +giving each a unique name: + +```bash {1,5} +thv run --name playwright-desktop playwright \ + -- --device "Desktop Chrome" --viewport-size 1920,1080 + +thv run --name playwright-iphone playwright \ + -- --device "iPhone 15" +``` + + + + +Create a basic Kubernetes manifest to deploy the Playwright MCP server using the +Streamable HTTP transport: + +```yaml title="playwright.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: playwright + namespace: toolhive-system +spec: + image: mcr.microsoft.com/playwright/mcp:v0.0.68 + transport: streamable-http + mcpPort: 8931 + proxyPort: 8080 + args: + - '--port' + - '8931' + - '--host' + - '0.0.0.0' + - '--allowed-hosts' + - '*' +``` + +:::note[Important] + +Don't remove the `--port 8931`, `--host 0.0.0.0`, or `--allowed-hosts "*"` +arguments. They are required to run the server using Streamable HTTP. + +::: + +Apply the manifest to your Kubernetes cluster: + +```bash +kubectl apply -f playwright.yaml +``` + +For production deployments with network restrictions, add the +`--allowed-origins` argument with a list of trusted domains: + +```yaml {18-19} title="playwright-restricted.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: playwright + namespace: toolhive-system +spec: + image: mcr.microsoft.com/playwright/mcp:v0.0.68 + transport: streamable-http + mcpPort: 8931 + proxyPort: 8080 + args: + - '--port' + - '8931' + - '--host' + - '0.0.0.0' + - '--allowed-hosts' + - '*' + - '--allowed-origins' + - 'example.com;trusted-domain.org' +``` + +Mount a persistent volume to save browser output files like screenshots and +traces: + +```yaml {18-19,24-27,30-33} title="playwright-with-volume.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: playwright + namespace: toolhive-system +spec: + image: mcr.microsoft.com/playwright/mcp:v0.0.68 + transport: streamable-http + mcpPort: 8931 + proxyPort: 8080 + args: + - '--port' + - '8931' + - '--host' + - '0.0.0.0' + - '--allowed-hosts' + - '*' + - '--output-dir' + - '/browser-output' + - '--save-trace' + - '--save-session' + podTemplateSpec: + spec: + volumes: + - name: playwright-output + persistentVolumeClaim: + claimName: playwright-output-claim + containers: + - name: mcp + volumeMounts: + - mountPath: /browser-output + name: playwright-output + readOnly: false +``` + + + + +## Sample prompts + +Here are some sample prompts you can use to interact with the Playwright MCP +server: + +- "Navigate to https://toolhive.dev and take a screenshot of the homepage" +- "Go to the login page, fill in the username field with 'testuser' and password + field with 'password123', then click the login button" +- "Open https://news.ycombinator.com and get the titles of the top 5 stories" +- "Navigate to a form on https://httpbin.org/forms/post, fill it out with sample + data, and submit it" +- "Go to https://github.com/microsoft/playwright and check if there are any + console errors on the page" +- "Take a full-page screenshot of https://example.com and save it as + 'homepage.png'" +- "Navigate to an e-commerce site, search for 'laptop', and capture information + about the first product" +- "Open multiple tabs, navigate to different websites in each, and then switch + between them" + +## Recommended practices + +- **Use accessibility-first interactions**: The server works best when you + describe elements by their accessible names, labels, or text content rather + than visual characteristics. +- **Leverage browser profiles**: For workflows requiring authentication, use + persistent profiles or storage state files to maintain login sessions. +- **Enable network restrictions**: Use `--allowed-origins` and + `--blocked-origins` to limit which domains the browser can access, especially + in production environments. +- **Monitor resource usage**: Browser automation can be resource-intensive; + consider using headless mode and limiting concurrent sessions. +- **Handle dynamic content**: Use the `browser_wait_for` tool when dealing with + content that loads asynchronously. +- **Organize output files**: Specify a custom output directory to keep + screenshots, PDFs, and traces organized. +- **Test responsively**: Use device emulation to test how web applications + behave on different screen sizes and devices. diff --git a/versioned_docs/version-1.0/toolhive/guides-registry/authentication.mdx b/versioned_docs/version-1.0/toolhive/guides-registry/authentication.mdx new file mode 100644 index 00000000..b98377b2 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-registry/authentication.mdx @@ -0,0 +1,552 @@ +--- +title: Authentication configuration +description: + How to configure authentication for the ToolHive Registry Server with + anonymous and OAuth modes +--- + +The Registry server provides secure-by-default authentication, defaulting to +OAuth mode to protect your registry. You can configure authentication to fit +different deployment scenarios, from development environments to production +deployments with enterprise identity providers. + +## Authentication modes + +The server supports two authentication modes configured via the required `auth` +section in your configuration file: + +- **OAuth** (default): Secure authentication using JWT tokens from identity + providers +- **Anonymous**: No authentication (development/testing only) + +:::info[Secure by default] + +The server defaults to **OAuth mode** when no explicit auth configuration is +provided. This secure-by-default posture ensures your registry is protected +unless you explicitly choose anonymous mode for development scenarios. + +::: + +## OAuth authentication + +OAuth mode (the default) validates access tokens from identity providers. The +server supports two token formats: + +- **JWT tokens**: Validated locally using the provider's public keys (JWKS + endpoint). This is the most common format for modern identity providers. +- **Opaque tokens**: Validated via token introspection (RFC 7662) by querying + the provider's introspection endpoint. Use this when your provider issues + non-JWT tokens. + +This enables enterprise authentication with providers like Keycloak, Auth0, +Okta, Azure AD, Kubernetes service accounts, or any OAuth-compliant service. + +### Basic OAuth configuration + +```yaml title="config-oauth.yaml" +auth: + mode: oauth + oauth: + resourceUrl: https://registry.example.com + providers: + - name: keycloak + issuerUrl: https://keycloak.example.com/realms/mcp + audience: registry-api +``` + +### OAuth configuration fields + +| Field | Type | Required | Default | Description | +| ----------------- | -------- | -------- | ----------------------------------------- | -------------------------------------------------- | +| `mode` | string | Yes | `oauth` | Authentication mode (`oauth` or `anonymous`) | +| `resourceUrl` | string | Yes | - | The URL of the registry resource being protected | +| `realm` | string | No | `mcp-registry` | OAuth realm identifier | +| `scopesSupported` | []string | No | `[mcp-registry:read, mcp-registry:write]` | OAuth scopes advertised in the discovery endpoint | +| `publicPaths` | []string | No | `[]` | Additional paths accessible without authentication | +| `providers` | array | Yes | - | List of OAuth/OIDC identity providers | + +### Provider configuration fields + +| Field | Type | Required | Description | +| ------------------ | ------ | -------- | ----------------------------------------------------------------------- | +| `name` | string | Yes | Provider identifier for logging and monitoring | +| `issuerUrl` | string | Yes | OAuth/OIDC issuer URL (e.g., `https://keycloak.example.com/realms/mcp`) | +| `audience` | string | Yes | Expected audience claim in the access token | +| `jwksUrl` | string | No | JWKS endpoint URL (skips OIDC discovery if specified) | +| `introspectionUrl` | string | No | Token introspection endpoint URL for opaque token validation | +| `clientId` | string | No | OAuth client ID (for authenticated introspection requests) | +| `clientSecretFile` | string | No | Path to file containing client secret (for authenticated introspection) | +| `caCertPath` | string | No | Path to CA certificate for TLS verification | +| `authTokenFile` | string | No | Path to token file for authenticating JWKS/introspection requests | +| `allowPrivateIP` | bool | No | Allow connections to private IP addresses (for in-cluster use) | + +### Complete OAuth configuration example + +```yaml title="config-oauth-complete.yaml" +auth: + mode: oauth + oauth: + resourceUrl: https://registry.example.com + realm: mcp-registry + scopesSupported: + - mcp-registry:read + - mcp-registry:write + publicPaths: + - /custom-health + - /metrics + providers: + - name: keycloak-prod + issuerUrl: https://keycloak.example.com/realms/production + audience: registry-api + clientId: registry-client + clientSecretFile: /etc/secrets/keycloak-secret + caCertPath: /etc/ssl/certs/keycloak-ca.crt + - name: keycloak-staging + issuerUrl: https://keycloak.example.com/realms/staging + audience: registry-api-staging +``` + +### Opaque token configuration + +When your identity provider issues opaque (non-JWT) tokens, configure the +`introspectionUrl` field to enable token introspection: + +```yaml title="config-opaque-tokens.yaml" +auth: + mode: oauth + oauth: + resourceUrl: https://registry.example.com + providers: + - name: google + issuerUrl: https://accounts.google.com + introspectionUrl: https://oauth2.googleapis.com/tokeninfo + audience: 407408718192.apps.googleusercontent.com +``` + +The server automatically detects the token format and uses the appropriate +validation method—attempting JWT validation first and falling back to token +introspection if needed. + +## Kubernetes authentication + +For Kubernetes deployments, you can configure OAuth to validate service account +tokens. This provides automatic, zero-config authentication for workloads +running in the cluster. + +### Kubernetes provider configuration + +```yaml title="config-k8s-auth.yaml" +auth: + mode: oauth + oauth: + resourceUrl: https://registry.example.com + providers: + - name: kubernetes + issuerUrl: https://kubernetes.default.svc.cluster.local + jwksUrl: https://kubernetes.default.svc/openid/v1/jwks + audience: registry-server + caCertPath: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + authTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token + allowPrivateIP: true +``` + +:::info[Kubernetes-specific configuration] + +- **issuerUrl**: Use `https://kubernetes.default.svc.cluster.local` to match the + `iss` claim in Kubernetes service account tokens. +- **jwksUrl**: Specify the JWKS endpoint directly to skip OIDC discovery. +- **authTokenFile**: The server uses this token to authenticate when fetching + the JWKS from the Kubernetes API server. +- **allowPrivateIP**: Required for in-cluster communication with the API server. + +::: + +### How Kubernetes authentication works + +1. Workloads mount projected service account tokens with a specific audience +2. Clients send these tokens in the `Authorization: Bearer ` header +3. The server validates tokens using the Kubernetes API server's public keys +4. Only tokens with the correct audience (e.g., `registry-server`) are accepted + +### Kubernetes deployment example + +When deploying in Kubernetes, the service account CA certificate is +automatically mounted: + +```yaml title="deployment-k8s-auth.yaml" +apiVersion: apps/v1 +kind: Deployment +metadata: + name: registry-api +spec: + replicas: 1 + selector: + matchLabels: + app: registry-api + template: + metadata: + labels: + app: registry-api + spec: + serviceAccountName: registry-api + containers: + - name: registry-api + image: ghcr.io/stacklok/thv-registry-api:latest + args: + - serve + - --config=/etc/registry/config.yaml + volumeMounts: + - name: config + mountPath: /etc/registry/config.yaml + subPath: config.yaml + readOnly: true + # Service account token and CA cert are mounted automatically by Kubernetes + volumes: + - name: config + configMap: + name: registry-api-config +``` + +The service account token and CA certificate are automatically mounted at: + +- Token: `/var/run/secrets/kubernetes.io/serviceaccount/token` +- CA cert: `/var/run/secrets/kubernetes.io/serviceaccount/ca.crt` + +### Client workload example + +Clients that need to authenticate with the registry should mount a projected +service account token with the correct audience: + +```yaml title="client-workload.yaml" +apiVersion: v1 +kind: Pod +metadata: + name: registry-client +spec: + serviceAccountName: my-client-sa + containers: + - name: client + image: my-client-image + volumeMounts: + - name: registry-token + mountPath: /var/run/secrets/registry + readOnly: true + volumes: + - name: registry-token + projected: + sources: + - serviceAccountToken: + audience: registry-server + expirationSeconds: 3600 + path: token +``` + +The client reads the token from `/var/run/secrets/registry/token` and includes +it in the `Authorization: Bearer ` header when making requests to the +registry. + +## Provider-specific examples + +### Keycloak + +```yaml +auth: + mode: oauth + oauth: + resourceUrl: https://registry.example.com + providers: + - name: keycloak + issuerUrl: https://keycloak.example.com/realms/YOUR_REALM + audience: registry-api +``` + +The `issuerUrl` should point to your Keycloak realm. The realm name is part of +the URL path. + +### Auth0 + +```yaml +auth: + mode: oauth + oauth: + resourceUrl: https://registry.example.com + providers: + - name: auth0 + issuerUrl: https://YOUR_DOMAIN.auth0.com/ + audience: https://registry.example.com +``` + +For Auth0, the `issuerUrl` is your Auth0 domain. The `audience` should match the +API identifier configured in your Auth0 dashboard. + +### Azure AD + +```yaml +auth: + mode: oauth + oauth: + resourceUrl: https://registry.example.com + providers: + - name: azure-ad + issuerUrl: https://login.microsoftonline.com/YOUR_TENANT_ID/v2.0 + audience: api://YOUR_CLIENT_ID +``` + +For Azure AD, the `issuerUrl` includes your tenant ID, and the `audience` +typically uses the `api://` scheme with your application's client ID. + +### Okta + +```yaml +auth: + mode: oauth + oauth: + resourceUrl: https://registry.example.com + providers: + - name: okta + issuerUrl: https://YOUR_DOMAIN.okta.com/oauth2/default + audience: api://default +``` + +For Okta, the `issuerUrl` points to your Okta authorization server. Use +`/oauth2/default` for the default authorization server or +`/oauth2/YOUR_AUTH_SERVER_ID` for custom servers. + +## Anonymous authentication + +Anonymous mode disables authentication entirely, allowing unrestricted access to +all registry endpoints. This is only suitable for development, testing, or +internal deployments where authentication is handled at a different layer (e.g., +network policies, VPN, or reverse proxy). + +### Anonymous configuration + +```yaml title="config-anonymous.yaml" +auth: + mode: anonymous +``` + +:::danger[No access control] + +Anonymous mode provides **no access control**. Only use it in trusted +environments or when other security measures are in place. **Do not use +anonymous mode in production.** + +::: + +### Anonymous use cases + +- Local development and testing +- Internal deployments behind corporate firewalls +- Read-only public registries +- Environments with external authentication (reverse proxy, API gateway) + +## Default public paths + +The following endpoints are **always accessible without authentication**, +regardless of the auth mode: + +- `/health` - Health check endpoint +- `/readiness` - Readiness probe endpoint +- `/version` - Version information +- `/.well-known/*` - OAuth discovery endpoints (RFC 9728) + +You can configure additional public paths using the `publicPaths` field in your +OAuth configuration. See the +[Registry API reference](../reference/registry-api.mdx) for complete endpoint +documentation. + +## RFC 9728 OAuth discovery + +The server implements [RFC 9728](https://www.rfc-editor.org/rfc/rfc9728.html) +for OAuth Protected Resource Metadata, enabling clients to automatically +discover authentication requirements. + +### Discovery endpoint + +Clients can discover the server's OAuth configuration at: + +```text +GET /.well-known/oauth-protected-resource +``` + +### Example discovery response + +```json +{ + "resource": "https://registry.example.com", + "authorization_servers": [ + "https://keycloak.example.com/realms/production", + "https://keycloak.example.com/realms/staging" + ], + "scopes_supported": ["mcp-registry:read", "mcp-registry:write"], + "bearer_methods_supported": ["header"], + "resource_documentation": "https://docs.example.com/registry" +} +``` + +This allows OAuth clients to automatically configure themselves without manual +setup, improving interoperability and reducing configuration errors. + +When a request fails authentication, the server returns a `WWW-Authenticate` +header that includes a link to the discovery endpoint, helping clients locate +the authentication requirements. + +## Testing authentication + +### Using curl with a bearer token + +```bash +TOKEN="your-jwt-token-here" + +curl -H "Authorization: Bearer $TOKEN" \ + https://registry.example.com/default/v0.1/servers +``` + +### Using kubectl with Kubernetes service accounts + +Use `kubectl create token` to generate a token with the correct audience: + +```bash +# Create a token with the registry-server audience +TOKEN=$(kubectl create token \ + -n \ + --audience=registry-server) + +# Make authenticated request +curl -H "Authorization: Bearer $TOKEN" \ + https://registry.example.com/registry/v0.1/servers +``` + +:::tip[Projected tokens vs kubectl create token] + +For automated workloads, use projected service account tokens (see +[Client workload example](#client-workload-example)). The `kubectl create token` +command is useful for manual testing and debugging. + +::: + +### Testing token validation + +To verify your token is valid: + +1. Decode the JWT at [jwt.io](https://jwt.io/) (don't paste production tokens!) +2. Check the `iss` (issuer) matches your configured `issuerUrl` +3. Check the `aud` (audience) matches your configured `audience` +4. Check the `exp` (expiration) is in the future + +## Choosing an authentication mode + +| Mode | Security | Complexity | Best for | +| --------- | -------- | ---------- | ----------------------------------------------- | +| OAuth | High | Medium | Production deployments, enterprise environments | +| Anonymous | None | None | Development, testing, internal trusted networks | + +**Recommendations:** + +- **Production deployments**: Always use OAuth mode with your organization's + identity provider +- **Kubernetes deployments**: Use OAuth with Kubernetes provider for automatic + authentication +- **Development/testing**: Use anonymous mode for local development only +- **Public read-only registries**: Use OAuth mode with rate limiting; avoid + anonymous in production + +## Security considerations + +### Token validation + +All OAuth providers validate: + +- Token expiration (`exp` claim) +- Audience claim (`aud`) matches configuration +- Issuer (`iss`) matches the configured provider + +For JWT tokens, signature verification uses the provider's public keys (fetched +from the issuer's JWKS endpoint). For opaque tokens, the server queries the +configured `introspectionUrl` to validate the token. + +### HTTPS requirements + +Always use HTTPS in production to protect tokens in transit: + +```yaml +auth: + mode: oauth + oauth: + resourceUrl: https://registry.example.com # Use HTTPS + providers: + - issuerUrl: https://keycloak.example.com/realms/mcp # Use HTTPS +``` + +### Token storage + +- Never log or persist JWT tokens in plaintext +- Use short-lived tokens when possible (e.g., 1 hour) +- Implement token refresh flows for long-running clients +- Rotate client secrets regularly if using `clientSecretFile` + +### Custom CA certificates + +If your identity provider uses a custom CA certificate, specify the `caCertPath` +in your provider configuration: + +```yaml +providers: + - name: internal-keycloak + issuerUrl: https://keycloak.internal.example.com/realms/mcp + audience: registry-api + caCertPath: /etc/ssl/certs/internal-ca.crt +``` + +## Troubleshooting + +### 401 Unauthorized errors + +If you receive 401 Unauthorized responses: + +1. **Verify the token is valid**: Check expiration, issuer, and audience claims +2. **Check the Authorization header**: Must be `Authorization: Bearer ` +3. **Verify issuer URL**: Must exactly match the `iss` claim in your token +4. **Check audience**: The `aud` claim must match your configured `audience` +5. **Review server logs**: Look for specific validation error messages + +### Token validation errors + +If you see authentication failures in server logs: + +1. **Issuer URL accessibility**: Verify the server can reach the issuer's JWKS + endpoint (typically `${issuerUrl}/.well-known/openid-configuration`) +2. **Network connectivity**: Check DNS resolution and firewall rules +3. **CA certificates**: If using custom CAs, ensure `caCertPath` is correct +4. **Token expiration**: Ensure your token hasn't expired (check `exp` claim) + +### Provider connectivity issues + +If the server cannot connect to the OAuth provider: + +1. Verify network connectivity to the issuer URL from the server +2. Check DNS resolution for the provider's domain +3. Ensure firewall rules allow outbound HTTPS (port 443) to the provider +4. For Kubernetes providers, verify the API server is reachable at + `https://kubernetes.default.svc` +5. Check CA certificate configuration if using self-signed certificates + +### Multiple providers not working + +If tokens from some providers work but others don't: + +1. Verify each provider's configuration independently using curl +2. Check that audience claims differ between providers if needed (or are + intentionally the same) +3. Ensure issuer URLs are correct and include the full path (including realm for + Keycloak) +4. Review server logs to identify which specific provider validation is failing +5. Test each provider's JWKS endpoint accessibility: + `curl ${issuerUrl}/.well-known/openid-configuration` + +## Next steps + +- [Configure database storage](./database.mdx) for production deployments +- [Deploy the server](./deployment.mdx) with authentication enabled +- [Configure registry sources](./configuration.mdx) for your environment diff --git a/versioned_docs/version-1.0/toolhive/guides-registry/authorization.mdx b/versioned_docs/version-1.0/toolhive/guides-registry/authorization.mdx new file mode 100644 index 00000000..a51a3964 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-registry/authorization.mdx @@ -0,0 +1,429 @@ +--- +title: Authorization +description: + Configure role-based access control and claims-based authorization for the + Registry Server +--- + +The Registry server provides a claims-based authorization model that controls +who can manage sources, registries, and entries. Authorization builds on top of +[authentication](./authentication.mdx) — you need OAuth authentication enabled +before configuring authorization. + +## How authorization works + +When a client accesses registry data, the server checks three layers in order: + +1. **Registry claims** (access gate) — can the caller access this registry at + all? If the registry has claims and the caller's JWT doesn't satisfy them, + the server returns `403 Forbidden` before any data is returned. +2. **Entry claims** (visibility filter) — which entries can the caller see? Each + entry can carry claims inherited from its source (for synced sources) or set + explicitly (via publish payload or Kubernetes annotation). Only entries whose + claims the caller's JWT satisfies are included in the response. +3. **Role checks** (admin operations) — can the caller perform this write + operation? Publishing, deleting, and managing sources/registries require + specific [roles](#configure-roles). + +When a caller makes an API request, the server: + +1. Extracts the caller's claims from their JWT token +2. Resolves the caller's roles based on those claims and the `authz` + configuration +3. Checks whether the caller's role permits the operation +4. Checks whether the caller's claims satisfy the resource's claims + +```mermaid +flowchart LR + JWT["JWT token"] --> Claims["Extract claims"] + Claims --> Roles["Resolve roles"] + Roles --> RoleCheck{"Role\npermitted?"} + RoleCheck -->|Yes| ClaimCheck{"Claims\nsatisfied?"} + RoleCheck -->|No| Deny403["403 Forbidden"] + ClaimCheck -->|Yes| Allow["Allow"] + ClaimCheck -->|No| Deny403 +``` + +## Configure roles + +Define roles in the `auth.authz.roles` section of your configuration file. Each +role maps to a list of claim rules — if a caller's JWT claims match any rule in +the list, they are granted that role. + +```yaml title="config-authz.yaml" +auth: + mode: oauth + oauth: + resourceUrl: https://registry.example.com + providers: + - name: keycloak + issuerUrl: https://keycloak.example.com/realms/mcp + audience: registry-api + # highlight-start + authz: + roles: + superAdmin: + - role: 'super-admin' + manageSources: + - org: 'acme' + role: 'admin' + manageRegistries: + - org: 'acme' + role: 'admin' + manageEntries: + - role: 'writer' + # highlight-end +``` + +### Available roles + +| Role | Grants access to | +| ------------------ | ------------------------------------------------------------- | +| `superAdmin` | All operations; bypasses all claim checks | +| `manageSources` | Create, update, delete, and list sources via the admin API | +| `manageRegistries` | Create, update, delete, and list registries via the admin API | +| `manageEntries` | Publish and delete MCP server versions and skills | + +### Role rule matching + +Each role is defined as a list of claim maps. A caller is granted the role if +their JWT claims match **any** map in the list (OR logic). Within a single map, +**all** key-value pairs must match (AND logic). + +```yaml title="Example: grant manageSources to org admins OR platform leads" +authz: + roles: + manageSources: + # Rule 1: any admin in the acme org + - org: 'acme' + role: 'admin' + # Rule 2: anyone with the platform-lead role + - role: 'platform-lead' +``` + +Claim values can be strings or arrays. When a JWT claim is an array (for +example, `role: ["admin", "writer"]`), the server checks whether any element +matches the required value. A rule with `role: "admin"` would match this JWT +because `"admin"` is one of the array elements. + +### Super-admin role + +The `superAdmin` role bypasses **all** claim checks across the entire server. A +super-admin can: + +- Access any registry regardless of its claims +- See all entries regardless of source or entry claims +- Manage any source or registry, even those with claims outside their JWT +- Publish and delete entries without claim validation + +Use this role sparingly and only for platform operators who need unrestricted +access. + +## Configure claims on sources and registries + +Claims are key-value pairs attached to sources and registries in your +configuration file. They act as access boundaries — only callers whose JWT +claims satisfy the resource's claims can access it. + +### Source claims + +For synced sources (Git, API, File), claims on a source are **inherited by all +entries** during sync. Every MCP server or skill ingested from that source +carries the source's claims. + +For Kubernetes and managed sources, source claims control who can manage the +source via the admin API but are **not** inherited by entries. Kubernetes +entries get claims from the +[`authz-claims` annotation](./configuration.mdx#per-entry-claims-via-annotation) +on each CRD. Managed source entries get claims from the publish request payload. + +```yaml title="config-source-claims.yaml" +sources: + - name: platform-tools + format: upstream + git: + repository: https://github.com/acme/platform-tools.git + branch: main + path: registry.json + syncPolicy: + interval: '30m' + # highlight-start + claims: + org: 'acme' + team: 'platform' + # highlight-end + + - name: data-tools + format: upstream + git: + repository: https://github.com/acme/data-tools.git + branch: main + path: registry.json + syncPolicy: + interval: '30m' + claims: + org: 'acme' + team: 'data' +``` + +With this configuration: + +- Entries from `platform-tools` are visible only to callers with `org: "acme"` + **and** `team: "platform"` in their JWT +- Entries from `data-tools` are visible only to callers with `org: "acme"` + **and** `team: "data"` in their JWT +- A super-admin sees all entries regardless of claims + +### Registry claims + +Claims on a registry act as an **access gate** for the consumer API. Before +returning any data from a registry's endpoints, the server checks that the +caller's JWT claims satisfy the registry's claims. + +```yaml title="config-registry-claims.yaml" +registries: + - name: platform + sources: ['platform-tools'] + # highlight-start + claims: + org: 'acme' + team: 'platform' + # highlight-end + + - name: public + sources: ['community-tools'] + # No claims — accessible to all authenticated users +``` + +A caller who requests `GET /platform/v0.1/servers` must have JWT claims that +include `org: "acme"` and `team: "platform"`. Otherwise, the server returns +`403 Forbidden`. + +### Claim containment + +The server uses **containment** (superset check) for claim validation: the +caller's claims must be a superset of the resource's claims. For example: + +| Resource claims | Caller JWT claims | Result | +| --------------------------------- | --------------------------------- | ------- | +| `{org: "acme"}` | `{org: "acme", team: "platform"}` | Allowed | +| `{org: "acme", team: "platform"}` | `{org: "acme"}` | Denied | +| `{}` (no claims) | `{org: "acme"}` | Allowed | +| `{org: "acme"}` | `{org: "contoso"}` | Denied | + +Registries and sources with no claims are accessible to all authenticated +callers. + +:::warning[Entries without claims are invisible when authorization is enabled] + +Entries with no claims are visible in anonymous mode and +[auth-only mode](#auth-only-mode), but **invisible** when full authorization is +enabled. The per-entry filter requires both sides to have claims for a match — +entries without claims are filtered out. To make entries visible, attach claims +to the source (for synced sources) or to individual entries (via the publish +payload or the +[`authz-claims` annotation](./configuration.mdx#per-entry-claims-via-annotation) +for Kubernetes sources). + +::: + +## Claims on published entries + +When you publish an MCP server version or skill to a managed source, you can +attach claims to the entry. The server enforces two rules: + +1. **Publish claims must be a subset of the publisher's JWT claims.** Every + claim key in the publish request must exist in the publisher's JWT with a + matching value. For example, if your JWT has + `{org: "acme", team: "platform"}`, you can publish entries with + `{org: "acme"}`, `{org: "acme", team: "platform"}`, or no claims at all — but + not with `{org: "contoso"}` (a value your JWT doesn't have). + +2. **Subsequent versions must have the same claims as the first.** Once you + publish the first version of an entry with specific claims, all future + versions must carry the exact same claims. This prevents accidentally + narrowing or broadening visibility across versions. + +```bash title="Publish a server with claims" +curl -X POST \ + https://registry.example.com/default/v0.1/publish \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "my-server", + "version": "1.0.0", + "url": "https://mcp.example.com/my-server", + "description": "Team-scoped MCP server", + "claims": { + "org": "acme", + "team": "platform" + } + }' +``` + +## Admin API claim scoping + +When authorization is enabled, the admin API endpoints for managing sources and +registries are also scoped by claims: + +- **List sources/registries**: Only returns resources whose claims the caller's + JWT satisfies. +- **Get source/registry by name**: Returns `404 Not Found` (not `403`) when the + caller's claims don't match — this prevents information disclosure about + resources the caller cannot access. +- **List source/registry entries**: `GET /v1/sources/{name}/entries` and + `GET /v1/registries/{name}/entries` return the raw entries (servers and skills + with versions and claims) in a source or registry. These endpoints follow the + same claim scoping — the parent source or registry must be accessible to the + caller. +- **Create source/registry**: The request claims must be a subset of the + caller's JWT claims. +- **Update/delete source/registry**: The caller's JWT claims must satisfy the + existing resource's claims. + +## Auth-only mode + +When OAuth authentication is enabled but the `auth.authz` block is omitted from +your configuration, the server runs in **auth-only mode**. In this mode: + +- Callers must still authenticate with a valid JWT token +- All claims-based filtering is disabled — every authenticated caller sees all + entries regardless of source or registry claims +- Role checks are not enforced (no roles are resolved) +- The server logs a warning at startup: + `Authorization not configured, per-entry claims filtering disabled (auth-only mode)` + +Auth-only mode is useful when you need identity verification without +multi-tenant visibility controls — for example, a single-team deployment where +all authenticated users should have the same access. + +To enable full authorization, add the `auth.authz` block with +[role definitions](#configure-roles). + +## Anonymous mode + +When authentication is set to `anonymous`, all authorization checks are +bypassed. There are no JWT claims to validate, so all sources, registries, and +entries are accessible without restriction. This is suitable for development and +testing environments only. + +## Check your identity and permissions + +Use the `GET /v1/me` endpoint to verify your authenticated identity and resolved +roles: + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + https://registry.example.com/v1/me +``` + +```json title="Example response" +{ + "subject": "user@example.com", + "roles": ["manageSources", "manageEntries"] +} +``` + +This is useful for debugging authorization issues — you can confirm which roles +your JWT grants and whether the expected claims are present. The endpoint +returns `401 Unauthorized` in anonymous mode since there is no identity to +report. + +## Complete example + +This example shows a multi-team setup with full RBAC and claims-based scoping: + +```yaml title="config-multi-tenant.yaml" +sources: + - name: platform-tools + format: upstream + git: + repository: https://github.com/acme/platform-tools.git + branch: main + path: registry.json + syncPolicy: + interval: '30m' + claims: + org: 'acme' + team: 'platform' + + - name: data-tools + format: upstream + git: + repository: https://github.com/acme/data-tools.git + branch: main + path: registry.json + syncPolicy: + interval: '30m' + claims: + org: 'acme' + team: 'data' + + - name: shared + managed: {} + +registries: + - name: platform + sources: ['platform-tools', 'shared'] + claims: + org: 'acme' + team: 'platform' + + - name: data + sources: ['data-tools', 'shared'] + claims: + org: 'acme' + team: 'data' + +auth: + mode: oauth + oauth: + resourceUrl: https://registry.example.com + providers: + - name: keycloak + issuerUrl: https://keycloak.example.com/realms/mcp + audience: registry-api + authz: + roles: + superAdmin: + - role: 'super-admin' + manageSources: + - org: 'acme' + role: 'admin' + manageRegistries: + - org: 'acme' + role: 'admin' + manageEntries: + - role: 'writer' +``` + +With this configuration: + +- **Platform team members** (JWT with `org: "acme"`, `team: "platform"`) can + access the `platform` registry and see entries from `platform-tools` and + `shared`. +- **Data team members** (JWT with `org: "acme"`, `team: "data"`) can access the + `data` registry and see entries from `data-tools` and `shared`. +- **Writers** (JWT with `role: "writer"`) can publish to the `shared` managed + source. +- **Admins** (JWT with `org: "acme"`, `role: "admin"`) can manage sources and + registries within the `acme` org. +- **Super-admins** (JWT with `role: "super-admin"`) can access and manage + everything. + +Entries published to the `shared` source without claims are visible through any +registry that includes it, subject only to the registry-level claims gate. To +restrict visibility further, attach claims when +[publishing entries](#claims-on-published-entries). + +## Next steps + +- [Configure authentication](./authentication.mdx) to set up OAuth providers +- [Configure sources and registries](./configuration.mdx) to set up your data + sources +- [Manage skills](./skills.mdx) to publish and discover reusable skills + +## Related information + +- [Registry server introduction](./intro.mdx) - architecture and features + overview diff --git a/versioned_docs/version-1.0/toolhive/guides-registry/configuration.mdx b/versioned_docs/version-1.0/toolhive/guides-registry/configuration.mdx new file mode 100644 index 00000000..60f2be00 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-registry/configuration.mdx @@ -0,0 +1,527 @@ +--- +title: Configuration +description: How to configure the ToolHive Registry Server with registry and sync policies +--- + +All configuration is done via YAML files. The server requires a `--config` flag +pointing to a YAML configuration file. + +## Configuration file structure + +```yaml title="config.yaml" +# Registry name/identifier (optional, defaults to "default") +registryName: my-registry + +# Registries configuration (required, can have multiple registries) +registries: + - name: toolhive + # Data format: upstream or toolhive (see below) + format: upstream + + # Git repository configuration + git: + repository: https://github.com/stacklok/toolhive-catalog.git + branch: main + path: pkg/catalog/toolhive/data/official-registry.json + + # Per-registry automatic sync policy (required for synced registries) + syncPolicy: + # Sync interval (e.g., "30m", "1h", "24h") + interval: '30m' + + # Optional: Per-registry server filtering + filter: + names: + include: ['official/*'] + exclude: ['*/deprecated'] + tags: + include: ['production'] + exclude: ['experimental'] + +# Authentication configuration (required) +# See authentication.mdx for detailed configuration options +auth: + mode: anonymous + +# Database configuration (required) +database: + host: localhost + port: 5432 + user: registry + database: registry + sslMode: require + maxOpenConns: 25 + maxIdleConns: 5 + connMaxLifetime: '5m' + maxMetaSize: 262144 + # Optional: dynamic authentication (alternative to pgpass) + # dynamicAuth: + # awsRdsIam: + # region: us-east-1 +``` + +## Command-line flags + +| Flag | Description | Required | Default | +| ------------- | ------------------------------------------- | -------- | ------- | +| `--config` | Path to YAML configuration file | Yes | - | +| `--address` | Server listen address | No | `:8080` | +| `--auth-mode` | Override auth mode (`anonymous` or `oauth`) | No | - | + +## Registry data formats + +The `format` field specifies the JSON schema format for the registry data: + +- **`upstream`**: The official + [upstream MCP registry format](../reference/registry-schema-upstream.mdx), + used by the MCP Registry API and recommended for new registries. +- **`toolhive`**: The + [ToolHive-native format](../reference/registry-schema-toolhive.mdx), used by + the built-in ToolHive registry. + +## Registries + +The server supports five registry types, each with its own configuration +options. You can configure multiple registries in a single server instance. + +### Git repository source + +Clone and sync from Git repositories. Ideal for version-controlled registries. + +:::note + +When the registry server clones a Git repository, it stores the local copy in +`/data`. Mounting a persistent volume there is optional but recommended for +production deployments. Without it, the registry server re-clones the repository +on every container restart, adding startup latency and increasing network usage. + +::: + +```yaml title="config-git.yaml" +registries: + - name: toolhive + format: toolhive + git: + repository: https://github.com/stacklok/toolhive-catalog.git + branch: main + path: pkg/catalog/toolhive/data/registry.json + syncPolicy: + interval: '30m' +``` + +**Configuration options:** + +- `repository` (required): Git repository URL +- `branch` (optional): Branch name to use (defaults to `main`) +- `tag` (optional): Tag name to pin to a specific version +- `commit` (optional): Commit SHA to pin to a specific commit +- `path` (optional): Path to the registry file within the repository (defaults + to `registry.json`) +- `auth` (optional): Authentication for private repositories (see below) + +:::tip + +You can use `branch`, `tag`, or `commit` to pin to a specific version. If +multiple are specified, `commit` takes precedence over `tag`, which takes +precedence over `branch`. + +::: + +#### Private repository authentication + +To access private Git repositories, configure the `auth` section with your +credentials: + +```yaml title="config-git-private.yaml" +registries: + - name: private-registry + format: toolhive + git: + repository: https://github.com/my-org/private-registry.git + branch: main + path: registry.json + # highlight-start + auth: + username: oauth2 + passwordFile: /secrets/git/token + # highlight-end + syncPolicy: + interval: '30m' +``` + +**Authentication options:** + +- `auth.username` (required with `passwordFile`): Git username for HTTP Basic + authentication. For GitHub and GitLab, use `oauth2` as the username when + authenticating with a personal access token (PAT). +- `auth.passwordFile` (required with `username`): Absolute path to a file + containing the Git password or token. Whitespace is trimmed from the file + content. + +:::warning + +Both `username` and `passwordFile` must be specified together. If only one is +provided, the configuration will fail validation. + +::: + +**Using with Kubernetes secrets:** + +In Kubernetes deployments, mount a secret containing your Git token and +reference the mount path: + +```yaml title="registry-deployment.yaml" +apiVersion: v1 +kind: Secret +metadata: + name: git-credentials +type: Opaque +stringData: + token: +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: registry-server +spec: + # Other deployment fields omitted, refer to the Deployment in Kubernetes guide + template: + spec: + containers: + - name: registry + volumeMounts: + - name: git-credentials + mountPath: /secrets/git + readOnly: true + - name: data + mountPath: /data + readOnly: false + volumes: + - name: git-credentials + secret: + secretName: git-credentials + items: + - key: token + path: token + - name: data + emptyDir: {} +``` + +### API endpoint source + +Sync from upstream MCP Registry APIs. Supports federation and aggregation +scenarios. + +```yaml title="config-api.yaml" +registries: + - name: mcp-upstream + format: upstream + api: + endpoint: https://registry.modelcontextprotocol.io + syncPolicy: + interval: '1h' +``` + +**Configuration options:** + +- `endpoint` (required): Base URL of the upstream MCP Registry API (without + path) +- `format` (required): Must be `upstream` for API sources + +:::note + +The server automatically appends the appropriate API paths (`/v0.1/servers`, +etc.) to the endpoint URL. + +::: + +### File source + +Read from filesystem. Ideal for local development and testing. + +```yaml title="config-file.yaml" +registries: + - name: local + format: upstream + file: + path: /data/registry.json + syncPolicy: + interval: '15m' +``` + +Alternatively, the source can be a custom URL. + +```yaml title="config-file.yaml" +registries: + - name: remote + format: upstream + file: + url: https://www.example.com/registry.json + timeout: 5s + syncPolicy: + interval: '15m' +``` + +The fields `file` and `url` are mutually exclusive. + +**Configuration options:** + +- `path` (required): Path to the registry file on the filesystem. Mutually + exclusive with `url` +- `url` (required): URL is the HTTP/HTTPS URL to fetch the registry file from. + Mutually exclusive with `file` +- `timeout` (optional): The timeout for HTTP requests when using URL, defaults + to 30s, ignored when `file` is specified + +### Managed registry + +API-managed registry for directly publishing and deleting MCP servers via the +API. Does not sync from external sources. + +```yaml title="config-managed.yaml" +registries: + - name: internal + format: upstream + managed: {} +``` + +**Configuration options:** + +- `managed` (required): Empty object to indicate managed registry type +- No sync policy required (managed registries are updated via API, not synced) + +**Supported operations:** + +- `POST /{registryName}/v0.1/publish` - Publish new server versions +- `DELETE /{registryName}/v0.1/servers/{name}/versions/{version}` - Delete + versions +- `GET` operations for listing and retrieving servers +- [Skills management](./skills.mdx) via the extensions API + +See the [Registry API reference](../reference/registry-api.mdx) for complete +endpoint documentation and request/response examples. + +### Kubernetes registry + +Discovers MCP servers from running Kubernetes deployments. Automatically creates +registry entries for deployed MCP servers in your cluster. + +:::note[Operator-managed registry] + +When using the ToolHive operator, a single Kubernetes registry named `default` +is automatically created and managed. You cannot configure additional Kubernetes +registries through the `MCPRegistry` CR or configuration file, as only one +Kubernetes registry instance is supported per Registry Server instance. + +The configuration example below is for reference when running the Registry +Server standalone (without the operator). + +::: + +By default, the Registry server discovers resources in all namespaces +(cluster-wide). You can restrict discovery to specific namespaces using the +`THV_REGISTRY_WATCH_NAMESPACE` environment variable. See +[Workload discovery](./deploy-manual.mdx#workload-discovery) for configuration +details and RBAC requirements. + +```yaml title="config-kubernetes.yaml" +registries: + - name: default + format: toolhive + kubernetes: {} +``` + +**Configuration options:** + +- `kubernetes` (required): Empty object to indicate Kubernetes registry type +- No sync policy required (Kubernetes registries query live deployments + on-demand) +- Only one Kubernetes registry is supported per Registry Server instance + +:::info[How does it work?] + +Kubernetes workload discovery works by looking for annotations in a specific set +of workloads. The types being watched are +[`MCPServer`](../guides-k8s/run-mcp-k8s.mdx), +[`MCPRemoteProxy`](../guides-k8s/remote-mcp-proxy.mdx), and +[`VirtualMCPServer`](../guides-vmcp/configuration.mdx). + +The Registry server will receive events when a resource among those listed above +is annotated with the following annotations: + +```yaml {7-16} +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: my-mcp-server + namespace: toolhive-system + annotations: + toolhive.stacklok.dev/registry-export: 'true' + toolhive.stacklok.dev/registry-title: 'Code Analysis Server' + toolhive.stacklok.dev/registry-url: 'https://mcp.example.com/servers/my-mcp-server' + toolhive.stacklok.dev/registry-description: | + Production MCP server for code analysis + toolhive.stacklok.dev/tool-definitions: + '[{"name":"analyze_code","description":"Analyze code for potential + issues","inputSchema":{"type":"object","properties":{"file":{"type":"string","description":"Path + to the file to + analyze"},"rules":{"type":"array","items":{"type":"string"},"description":"List + of rule IDs to check"}},"required":["file"]}}]' +spec: + # ... MCP server spec +``` + +| Annotation | Required | Description | +| -------------------------------------------- | -------- | --------------------------------------------------------------------------------- | +| `toolhive.stacklok.dev/registry-export` | Yes | Must be `"true"` to include in registry | +| `toolhive.stacklok.dev/registry-url` | Yes | The external endpoint URL for this server | +| `toolhive.stacklok.dev/registry-description` | Yes | Description text displayed in registry listings | +| `toolhive.stacklok.dev/registry-title` | No | Human-friendly display name for the registry entry (overrides the generated name) | +| `toolhive.stacklok.dev/tools` | No | JSON array of tool name strings (e.g., `["get_weather","get_forecast"]`) | +| `toolhive.stacklok.dev/tool-definitions` | No | JSON array of tool definitions with MCP tool metadata (name, description, schema) | + +**Tool definitions format:** + +The `tool-definitions` annotation accepts a JSON array containing tool metadata +that follows the MCP specification. Each tool definition can include: + +- `name` (required): Tool identifier +- `description`: Human-readable description of what the tool does +- `inputSchema`: JSON Schema describing the tool's input parameters +- `outputSchema`: JSON Schema describing the tool's output format +- `annotations`: Additional metadata about the tool + +The Registry server validates JSON syntax only. Schema validation is performed +by the operator during resource reconciliation. If the annotation contains +invalid JSON, a warning is logged and the tool definitions are omitted from the +registry, but the server is still registered normally. + +Tool definitions appear in the Registry API response under the server's registry +URL within the publisher-provided metadata: + +```json +{ + "_meta": { + "io.modelcontextprotocol.registry/publisher-provided": { + "io.github.stacklok": { + "https://mcp.example.com/servers/my-mcp-server": { + "tool_definitions": [...] + } + } + } + } +} +``` + +The registry URL from the `registry-url` annotation becomes a key in the JSON +structure. + +This feature requires the Registry server to be granted access to those +resources via a Service Account, check the details in the +[deployment section](./deploy-manual.mdx#workload-discovery). + +::: + +**Use cases:** + +- Discover MCP servers deployed via ToolHive Operator +- Automatically expose running MCP servers to end users +- Separate MCP server development from user consumption + +## Sync policy + +Configure automatic synchronization of registry data on a per-registry basis. +Only applicable to synced registries (Git, API, File). Not required for managed +or Kubernetes registries. + +```yaml +registries: + - name: toolhive + git: + repository: https://github.com/stacklok/toolhive-catalog.git + branch: main + path: pkg/catalog/toolhive/data/registry.json + syncPolicy: + # Sync interval (e.g., "30m", "1h", "24h") + interval: '30m' +``` + +The `interval` field specifies how often the server should fetch updates from +the registry. Use Go duration format (e.g., `"30m"`, `"1h"`, `"24h"`). + +:::note + +Sync policy is per-registry and must be specified within each registry +configuration. Managed and Kubernetes registries do not require sync policies. + +::: + +## Server filtering + +Optionally filter which servers are exposed through the API on a per-registry +basis. Only applicable to synced registries (Git, API, File). + +```yaml +registries: + - name: toolhive + git: + repository: https://github.com/stacklok/toolhive-catalog.git + branch: main + path: pkg/catalog/toolhive/data/registry.json + syncPolicy: + interval: '30m' + filter: + names: + include: ['official/*'] + exclude: ['*/deprecated'] + tags: + include: ['production'] + exclude: ['experimental'] +``` + +**Filter options:** + +- `names.include`: List of name patterns to include (supports wildcards) +- `names.exclude`: List of name patterns to exclude (supports wildcards) +- `tags.include`: List of tags that servers must have +- `tags.exclude`: List of tags that servers must not have + +:::note + +Filters are per-registry and specified within each registry configuration. + +::: + +## Authentication configuration + +The server supports multiple authentication modes to fit different deployment +scenarios. All authentication configuration is done via the `auth` section in +your configuration file. + +Available modes: + +- **Anonymous**: No authentication (development/testing) +- **OAuth with Kubernetes**: Service account token validation +- **OAuth with generic providers**: Integration with Keycloak, Auth0, Okta, etc. + +See the [Authentication configuration](./authentication.mdx) guide for detailed +information on configuring each mode. + +## Database configuration + +The server requires a PostgreSQL database for storing registry state and +metadata. See the [Database configuration](./database.mdx) guide for detailed +information. + +## Telemetry configuration + +The server supports OpenTelemetry for comprehensive observability, including +distributed tracing and metrics collection. See the +[Telemetry and metrics](./telemetry-metrics.mdx) guide for detailed information. + +## Next steps + +- [Configure authentication](./authentication.mdx) for secure access +- [Configure telemetry](./telemetry-metrics.mdx) for metrics and tracing +- [Deploy the server](./deployment.mdx) with your configuration +- [Configure database storage](./database.mdx) diff --git a/versioned_docs/version-1.0/toolhive/guides-registry/database.mdx b/versioned_docs/version-1.0/toolhive/guides-registry/database.mdx new file mode 100644 index 00000000..24d19b1b --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-registry/database.mdx @@ -0,0 +1,310 @@ +--- +title: Database configuration +description: + How to configure PostgreSQL database storage and migrations for the ToolHive + Registry server +--- + +The Registry server requires a PostgreSQL database for storing registry state +and metadata. This enables persistence across restarts and provides a foundation +for advanced features. + +## Configuration + +### Basic database configuration + +```yaml title="config.yaml" +database: + host: localhost + port: 5432 + user: registry + database: registry + sslMode: require + maxOpenConns: 25 + maxIdleConns: 5 + connMaxLifetime: '5m' + maxMetaSize: 262144 +``` + +### Configuration fields + +| Field | Type | Required | Default | Description | +| ----------------- | ------ | -------- | --------- | -------------------------------------------------------------------------------------------------- | +| `host` | string | Yes | - | Database server hostname or IP address | +| `port` | int | Yes | - | Database server port | +| `user` | string | Yes | - | Database username for normal operations | +| `migrationUser` | string | No | `user` | Database username for running migrations (should have elevated privileges) | +| `database` | string | Yes | - | Database name | +| `sslMode` | string | No | `require` | SSL mode (`disable`, `require`, `verify-ca`, `verify-full`) | +| `maxOpenConns` | int | No | `25` | Maximum number of open connections to the database | +| `maxIdleConns` | int | No | `5` | Maximum number of idle connections in the pool | +| `connMaxLifetime` | string | No | `5m` | Maximum lifetime of a connection (e.g., "1h", "30m") | +| `maxMetaSize` | int | No | `262144` | Maximum allowed size in bytes for publisher-provided metadata extensions (256 KB) | +| `dynamicAuth` | object | No | - | Dynamic authentication configuration (see [Dynamic authentication](#dynamic-authentication) below) | + +\* Password configuration is required but has multiple sources (see Password +Security and Dynamic authentication below) + +## Password security + +The server supports secure password management with separate credentials for +migrations and normal operations. This follows the principle of least privilege +by using elevated privileges only when necessary. + +Password configuration is done using a +[Postgres Password File](https://www.postgresql.org/docs/current/libpq-pgpass.html) +and exporting the `PGPASSFILE` environment variable. + +### Recommended setup + +For production deployments, use separate database users: + +1. **Application user** (`user`): Limited privileges for normal operations + - SELECT, INSERT, UPDATE, DELETE on application tables + - No schema modification privileges + +2. **Migration user** (`migrationUser`): Elevated privileges for migrations + - CREATE, ALTER, DROP on schemas and tables + - Used only during migration operations + +### Example configuration with separate users + +```yaml title="config-production.yaml" +database: + host: db.example.com + port: 5432 + user: db_app + migrationUser: db_migrator + database: registry + sslMode: verify-full +``` + +Store passwords in a pgpass file with restricted permissions: + +```bash +# Create pgpass file (recommended location: /etc/secrets/pgpassfile) +echo "db.example.com:5432:registry:db_app:app_password" > /etc/secrets/pgpassfile +echo "db.example.com:5432:registry:db_migrator:migrator_password" >> /etc/secrets/pgpassfile + +# Mandatory: restrict permissions to 0600, will be ignored otherwise +chmod 600 /etc/secrets/pgpassfile +``` + +**Using the pgpass file:** + +Set the `PGPASSFILE` environment variable when running the server: + +```bash +# For standalone server +export PGPASSFILE=/etc/secrets/pgpassfile +thv-registry-api serve --config config.yaml + +# For Docker/Kubernetes +# Set the PGPASSFILE environment variable in your deployment configuration +# See deployment.mdx for examples +``` + +:::tip + +The pgpass file format is: `hostname:port:database:username:password` + +You can use wildcards (`*`) for any field except password. For example: + +- `*:5432:*:db_app:app_password` - matches any host or database +- `localhost:*:registry:db_app:app_password` - matches any port + +See the +[PostgreSQL documentation](https://www.postgresql.org/docs/current/libpq-pgpass.html) +for more details. + +::: + +You can find more details about user creation and initial configuration in this +[test file](https://github.com/stacklok/toolhive-registry-server/blob/301ccf4e3ad13daad28be7b669d8e5fca337ec14/cmd/thv-registry-api/app/serve_test.go#L56-L103). + +## Dynamic authentication + +As an alternative to pgpass files, the server supports dynamic credential +generation for cloud-hosted databases. + +### AWS RDS IAM authentication + +When running on AWS, you can authenticate to RDS using IAM credentials instead +of static passwords. The server generates short-lived authentication tokens +using the IAM role attached to the workload. + +```yaml title="config-aws-rds.yaml" +database: + host: my-database.123456789.us-east-1.rds.amazonaws.com + port: 5432 + user: my_app_user + database: registry + sslMode: require + dynamicAuth: + awsRdsIam: + region: us-east-1 +``` + +**Configuration options:** + +- `dynamicAuth.awsRdsIam.region` (required): The AWS region of the RDS instance. + Set to `detect` to automatically determine the region from the EC2 instance + metadata service (IMDS). + +:::note + +Dynamic authentication replaces pgpass files. The server generates +authentication tokens automatically before each connection. + +::: + +## Database migrations + +The server uses database migrations to manage schema changes. Migrations run +automatically on startup, but you can also run them manually. + +### Automatic migrations + +By default, the server runs migrations automatically when it starts: + +1. Connects to the database using the migration user credentials +2. Checks the current migration version +3. Applies any pending migrations +4. Switches to the application user for normal operations + +This ensures the database schema is always up to date. + +### Manual migrations + +You can run migrations manually using the CLI: + +#### Run migrations + +```bash +thv-registry-api migrate up --config config.yaml [--yes] +``` + +The `--yes` flag skips the confirmation prompt. + +#### Rollback migrations + +```bash +thv-registry-api migrate down --config config.yaml --num-steps N [--yes] +``` + +The `--num-steps` parameter specifies how many migration steps to roll back. + +### Migration user privileges + +The migration user needs the following privileges: + +- CREATE, ALTER, DROP on the target database +- Ability to create and modify tables, indexes, and other schema objects +- SELECT, INSERT, UPDATE, DELETE on the migration tracking table + +Example SQL to create a migration user: + +```sql +DO $$ +DECLARE + migrator_user TEXT := 'db_migrator'; + migrator_password TEXT := 'migrator_password'; + db_name TEXT := 'registry'; +BEGIN + EXECUTE format('CREATE USER %I WITH PASSWORD %L', migrator_user, migrator_password); + EXECUTE format('GRANT CONNECT ON DATABASE %I TO %I', db_name, migrator_user); + EXECUTE format('GRANT CREATE ON SCHEMA public TO %I', migrator_user); + EXECUTE format('GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO %I', migrator_user); +END +$$; +``` + +### Application user privileges + +The application user needs limited privileges for normal operations: + +- SELECT, INSERT, UPDATE, DELETE on application tables +- No schema modification privileges + +Example SQL to create an application user: + +```sql +DO $$ +DECLARE + app_user TEXT := 'db_app'; + app_password TEXT := 'app_password'; + db_name TEXT := 'registry'; +BEGIN + CREATE ROLE toolhive_registry_server; + EXECUTE format('CREATE USER %I WITH PASSWORD %L', app_user, app_password); + EXECUTE format('GRANT toolhive_registry_server TO %I', app_user); + EXECUTE format('GRANT CONNECT ON DATABASE %I TO %I', db_name, app_user); +END +$$; +``` + +## SSL/TLS configuration + +Configure SSL/TLS for secure database connections: + +- `disable`: No SSL (not recommended for production) +- `require`: Require SSL (default) +- `verify-ca`: Require SSL and verify CA certificate +- `verify-full`: Require SSL and verify both CA and server hostname + +For production, use `verify-full`: + +```yaml +database: + sslMode: verify-full +``` + +## Connection pooling + +Tune connection pool settings for your workload: + +```yaml +database: + maxOpenConns: 25 # Maximum open connections + maxIdleConns: 5 # Maximum idle connections + connMaxLifetime: '5m' # Maximum connection lifetime +``` + +**Guidelines:** + +- `maxOpenConns`: Set based on your database server's connection limits +- `maxIdleConns`: Typically 20-25% of `maxOpenConns` +- `connMaxLifetime`: Set to less than your database server's connection timeout + +## Troubleshooting + +### Connection errors + +If you encounter connection errors: + +1. Verify database credentials are correct +2. Check network connectivity to the database server +3. Ensure the database server allows connections from your host +4. Verify SSL/TLS configuration matches your database server settings + +### Migration errors + +If migrations fail: + +1. Check that the migration user has sufficient privileges +2. Verify the database exists and is accessible +3. Check migration logs for specific error messages +4. Ensure no other processes are modifying the schema concurrently + +### Permission errors + +If you see permission errors during normal operations: + +1. Verify the application user has the required privileges +2. Check that migrations completed successfully +3. Ensure the application user can access all required tables + +## Next steps + +- [Deploy the server](./deployment.mdx) with database configuration +- [Configure data sources](./configuration.mdx) to populate the registry diff --git a/versioned_docs/version-1.0/toolhive/guides-registry/deploy-manual.mdx b/versioned_docs/version-1.0/toolhive/guides-registry/deploy-manual.mdx new file mode 100644 index 00000000..c5a4164d --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-registry/deploy-manual.mdx @@ -0,0 +1,361 @@ +--- +title: Deploy manually +description: How to deploy the ToolHive Registry Server in Kubernetes using raw manifests +--- + +:::info + +For an alternative deployment approach using Kubernetes custom resources, see +[Deploy with the ToolHive Operator](./deploy-operator.mdx). + +::: + +Below is an example Kubernetes Deployment configuring the ToolHive Registry +Server to expose a single static registry based on a Git repository. + +This example assumes that a Postgres database is available at `db.example.com` +and the necessary users for migration and application execution are configured +and able to connect to a `registry` database. It also assumes that you have a +keycloak instance configured to act as identity provider. + +All resources are created in the `toolhive-system` namespace. This namespace +must exist before applying the deployment. + +For further details about user grants read the +[Migration user privileges](./database.mdx#migration-user-privileges) and +[Application user privileges](./database.mdx#application-user-privileges) +sections. + +{/* prettier-ignore */} +```yaml title="deployment.yaml" +apiVersion: apps/v1 +kind: Deployment +metadata: + name: registry-api + namespace: toolhive-system +spec: + replicas: 1 + selector: + matchLabels: + app: registry-api + template: + metadata: + labels: + app: registry-api + spec: + initContainers: + - name: pgpass-fixer + image: alpine:3 + command: + - /bin/sh + - -c + - cp /cfg/* /thv/ && chmod 0600 /thv/pgpass && chown 65532:65532 /thv/pgpass + volumeMounts: + - name: thv + mountPath: /thv + - name: config + mountPath: /cfg/config.yaml + subPath: config.yaml + - name: pgpass + mountPath: /cfg/pgpass + subPath: pgpass + containers: + - name: registry-api + image: ghcr.io/stacklok/thv-registry-api:latest + args: + - serve + - --config=/thv/config.yaml + env: + - name: PGPASSFILE + value: /thv/pgpass + ports: + - containerPort: 8080 + name: http + volumeMounts: + - name: thv + mountPath: /thv + readOnly: true + livenessProbe: + httpGet: + path: /health + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /readiness + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 5 + volumes: + - name: thv + emptyDir: {} + - name: config + configMap: + name: registry-api-config + items: + - key: config.yaml + path: config.yaml + - name: pgpass + secret: + secretName: registry-api-pgpass + items: + - key: pgpass + path: pgpass +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: registry-api-config + namespace: toolhive-system +data: + config.yaml: | + registryName: my-registry + registries: + - name: git-registry + format: toolhive + git: + repository: https://github.com/stacklok/toolhive-catalog.git + branch: main + path: pkg/catalog/toolhive/data/registry.json + syncPolicy: + interval: "15m" + auth: + mode: oauth + oauth: + resourceUrl: https://registry.example.com + providers: + - name: keycloak + issuerUrl: https://keycloak.example.com/realms/mcp + audience: registry-api + database: + host: db.example.com + port: 5432 + user: db_app + migrationUser: db_migrator + database: registry + sslMode: verify-full +--- +apiVersion: v1 +kind: Secret +metadata: + name: registry-api-pgpass + namespace: toolhive-system +type: Opaque +stringData: + pgpass: | + db.example.com:5432:registry:db_app:app_password + db.example.com:5432:registry:db_migrator:migrator_password +--- +apiVersion: v1 +kind: Service +metadata: + name: registry-api + namespace: toolhive-system +spec: + selector: + app: registry-api + ports: + - port: 80 + targetPort: 8080 + protocol: TCP + type: ClusterIP +``` + +Apply the deployment: + +```bash +kubectl apply -f deployment.yaml +``` + +## Workload discovery + +Kubernetes workload discovery works by looking for annotations in a specific set +of workloads. The types being watched are +[`MCPServer`](../guides-k8s/run-mcp-k8s.mdx), +[`MCPRemoteProxy`](../guides-k8s/remote-mcp-proxy.mdx), and +[`VirtualMCPServer`](../guides-vmcp/configuration.mdx). + +By default, the Registry server discovers resources in all namespaces +(cluster-wide). You can restrict discovery to specific namespaces by setting the +`THV_REGISTRY_WATCH_NAMESPACE` environment variable to a comma-separated list of +namespace names in your deployment: + +```yaml +env: + - name: THV_REGISTRY_WATCH_NAMESPACE + value: toolhive-system,production +``` + +When `THV_REGISTRY_WATCH_NAMESPACE` is set, only resources in the specified +namespaces are discovered. When unset, the server watches all namespaces. + +Both RBAC options below use the same ClusterRole for workload discovery and a +separate namespace-scoped Role for leader election. The difference is how the +ClusterRole is bound. + +### Cluster-wide discovery (default) + +For cluster-wide discovery, apply the following resources: + +{/* prettier-ignore */} +```yaml title="registry-rbac-cluster.yaml" +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + toolhive.stacklok.io/registry-name: registry-api + name: registry-api + namespace: toolhive-system +--- +# Manager role for workload discovery (ToolHive CRDs + services) +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + toolhive.stacklok.io/registry-name: registry-api + name: registry-api-manager +rules: + - apiGroups: + - toolhive.stacklok.dev + resources: + - mcpservers + - mcpremoteproxies + - virtualmcpservers + verbs: + - get + - list + - watch + - apiGroups: + - '' + resources: + - services + verbs: + - get + - list + - watch +--- +# Leader election role (namespace-scoped, always required) +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + toolhive.stacklok.io/registry-name: registry-api + name: registry-api-leader-election + namespace: toolhive-system +rules: + - apiGroups: + - '' + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - '' + resources: + - events + verbs: + - create + - patch +--- +# Leader election binding (always namespace-scoped) +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + toolhive.stacklok.io/registry-name: registry-api + name: registry-api-leader-election + namespace: toolhive-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: registry-api-leader-election +subjects: + - kind: ServiceAccount + name: registry-api + namespace: toolhive-system +--- +# Cluster-wide binding for the manager role +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + toolhive.stacklok.io/registry-name: registry-api + name: registry-api-manager +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: registry-api-manager +subjects: + - kind: ServiceAccount + name: registry-api + namespace: toolhive-system +``` + +### Namespace-scoped discovery + +When `THV_REGISTRY_WATCH_NAMESPACE` is set, use the same ClusterRole but bind it +with a RoleBinding in each watched namespace instead of a ClusterRoleBinding. +Create one RoleBinding per namespace: + +{/* prettier-ignore */} +```yaml title="registry-rbac-namespace.yaml" +# Use the same ServiceAccount, ClusterRole, and leader election +# Role/RoleBinding from the cluster-wide example above. +# Replace the ClusterRoleBinding with one RoleBinding per namespace: +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + toolhive.stacklok.io/registry-name: registry-api + name: registry-api-manager + namespace: toolhive-system # repeat for each watched namespace +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: registry-api-manager +subjects: + - kind: ServiceAccount + name: registry-api + namespace: toolhive-system +``` + +### Applying the service account + +Apply the service account to the registry server deployment in the +`spec.template.spec` section: + +```yaml +spec: + template: + spec: + serviceAccountName: registry-api +``` + +:::tip + +If you run multiple Registry Server instances in the same namespace, set the +`THV_REGISTRY_LEADER_ELECTION_ID` environment variable to a unique value for +each instance to avoid leader election lease conflicts. The Helm chart handles +this automatically. + +::: diff --git a/versioned_docs/version-1.0/toolhive/guides-registry/deploy-operator.mdx b/versioned_docs/version-1.0/toolhive/guides-registry/deploy-operator.mdx new file mode 100644 index 00000000..a75704ad --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-registry/deploy-operator.mdx @@ -0,0 +1,730 @@ +--- +title: Deploy with the ToolHive Operator +description: + How to deploy the ToolHive Registry Server in Kubernetes using the ToolHive + Operator +--- + +## Prerequisites + +- A Kubernetes cluster (current and two previous minor versions are supported) +- Permissions to create resources in the cluster +- [`kubectl`](https://kubernetes.io/docs/tasks/tools/) configured to communicate + with your cluster +- The ToolHive operator installed in your cluster (see + [Deploy the operator](../guides-k8s/deploy-operator.mdx)) +- A PostgreSQL database + +## Overview + +The ToolHive operator deploys the Registry server in Kubernetes by creating +`MCPRegistry` resources. Alternatively, you can deploy the Registry Server +manually by following the [manual deployment instructions](./deploy-manual.mdx). + +### High-level architecture + +This diagram shows the basic relationship between components. The ToolHive +operator watches for `MCPRegistry` resources and automatically creates the +necessary infrastructure to run the Registry server. + +```mermaid +flowchart LR + subgraph K8s["Kubernetes Cluster"] + direction LR + subgraph NS["toolhive-system"] + Operator["ToolHive Operator"] + end + subgraph NS2["Registry
Namespace"] + Registry["Registry Server"] + DB[("PostgreSQL")] + end + subgraph Sources["Registry Sources"] + Git["Git Repository"] + CM["ConfigMap"] + PVC["PVC"] + API["Upstream API"] + end + end + + Operator -.->|creates| Registry + Sources -->|sync| Registry + Registry --> DB +``` + +## Create a registry + +You can create `MCPRegistry` resources in the namespaces where the ToolHive +Operator is deployed. + +See +[Deploy the operator](../guides-k8s/deploy-operator.mdx#operator-deployment-modes) +to learn about the different deployment modes. + +To create a registry, define an `MCPRegistry` resource and apply it to your +cluster. This minimal example creates a registry that syncs from the ToolHive +Git repository. + +```yaml title="my-registry.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRegistry +metadata: + name: my-registry + namespace: my-namespace # Update with your namespace +spec: + displayName: My MCP Registry + auth: + mode: anonymous + registries: + - name: toolhive + format: toolhive + git: + repository: https://github.com/stacklok/toolhive-catalog.git + branch: main + path: pkg/catalog/toolhive/data/registry.json + syncPolicy: + interval: '30m' +``` + +Apply the resource: + +```bash +kubectl apply -f my-registry.yaml +``` + +:::info[What's happening?] + +When you apply an `MCPRegistry` resource, here's what happens: + +1. The ToolHive operator detects the new resource (if it's in an allowed + namespace) +2. The operator creates the necessary RBAC resources in the target namespace +3. The operator creates a Deployment containing the Registry server pod and + service +4. The Registry server syncs data from the configured sources +5. The Registry API becomes available at the service endpoint + +::: + +## Configuring source Registries + +The `MCPRegistry` resource supports multiple registry source types. You can +configure one or more of them. Each type is mutually exclusive within a single +registry configuration. + +### Git repository source + +Clone and sync from Git repositories. Ideal for version-controlled registries. + +```yaml {9-14} title="registry-git.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRegistry +metadata: + name: git-registry +spec: + auth: + mode: anonymous + registries: + - name: toolhive + format: toolhive + git: + repository: https://github.com/stacklok/toolhive-catalog.git + branch: main + path: pkg/catalog/toolhive/data/registry.json + syncPolicy: + interval: '30m' +``` + +**Git source fields:** + +| Field | Required | Description | +| ------------ | -------- | ----------------------------------------------------- | +| `repository` | Yes | Git repository URL (HTTP/HTTPS/SSH) | +| `branch` | No | Branch name (mutually exclusive with `tag`, `commit`) | +| `tag` | No | Tag name (mutually exclusive with `branch`, `commit`) | +| `commit` | No | Commit SHA (mutually exclusive with `branch`, `tag`) | +| `path` | No | Path to registry file (default: `registry.json`) | + +:::tip + +You can use `branch`, `tag`, or `commit` to pin to a specific version. If +multiple are specified, `commit` takes precedence over `tag`, which takes +precedence over `branch`. + +::: + +### ConfigMap source + +Read from a Kubernetes ConfigMap. Ideal for registry data managed within the +cluster. + +```yaml {7-11} title="registry-configmap.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRegistry +metadata: + name: configmap-registry +spec: + auth: + mode: anonymous + registries: + - name: local + format: upstream + configMapRef: + name: registry-data + key: registry.json + syncPolicy: + interval: '15m' +``` + +The ConfigMap must exist in the same namespace as the `MCPRegistry` resource. + +### PersistentVolumeClaim source + +Read from a PersistentVolumeClaim. Useful for large registry files or shared +storage scenarios. + +```yaml {7-10} title="registry-pvc.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRegistry +metadata: + name: pvc-registry +spec: + auth: + mode: anonymous + registries: + - name: shared + format: upstream + pvcRef: + claimName: registry-data-pvc + path: registries/production.json + syncPolicy: + interval: '1h' +``` + +**PVC source fields:** + +| Field | Required | Description | +| ----------- | -------- | ----------------------------------------------------------- | +| `claimName` | Yes | Name of the PersistentVolumeClaim | +| `path` | No | Path to registry file within PVC (default: `registry.json`) | + +The PVC must exist in the same namespace as the `MCPRegistry` resource. + +### API source + +Sync from an upstream MCP Registry API. Supports federation and aggregation +scenarios. + +```yaml {7-10} title="registry-api.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRegistry +metadata: + name: api-registry +spec: + auth: + mode: anonymous + registries: + - name: upstream + format: upstream + api: + endpoint: https://registry.example.com + syncPolicy: + interval: '1h' +``` + +The controller automatically appends the appropriate API paths to the endpoint +URL. + +### Configure synchronization + +Each registry source can have its own sync policy that controls automatic +synchronization. + +```yaml +syncPolicy: + interval: '30m' # Go duration format: "1h", "30m", "24h" +``` + +### Filter registry content + +You can filter which servers are exposed through the API using name and tag +patterns. + +```yaml {13-20} title="registry-filtered.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRegistry +metadata: + name: filtered-registry +spec: + auth: + mode: anonymous + registries: + - name: toolhive + format: toolhive + git: + repository: https://github.com/stacklok/toolhive-catalog.git + branch: main + path: pkg/catalog/toolhive/data/registry.json + filter: + names: + include: + - 'official/*' + exclude: + - '*/deprecated' + tags: + include: + - production + exclude: + - experimental + syncPolicy: + interval: '30m' +``` + +## Configure database storage + +Configure PostgreSQL database storage for the Registry server. + +```yaml {17-32} title="registry-with-database.yaml" +apiVersion: v1 +kind: Secret +metadata: + name: registry-api-db-passwords +type: Opaque +stringData: + db-password: app_password + migration-password: migrator_password +--- +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRegistry +metadata: + name: production-registry +spec: + auth: + mode: anonymous + databaseConfig: + host: postgres.database.svc.cluster.local + port: 5432 + user: db_app + migrationUser: db_migrator + dbAppUserPasswordSecretRef: + name: registry-api-db-passwords + key: db-password + dbMigrationUserPasswordSecretRef: + name: registry-api-db-passwords + key: migration-password + database: registry + sslMode: verify-full + maxOpenConns: 25 + maxIdleConns: 5 + connMaxLifetime: '30m' + registries: + - name: toolhive + format: toolhive + git: + repository: https://github.com/stacklok/toolhive-catalog.git + branch: main + path: pkg/catalog/toolhive/data/registry.json + syncPolicy: + interval: '30m' +``` + +**Database configuration fields:** + +| Field | Default | Description | +| ---------------------------------- | ------------- | ------------------------------------------------- | +| `host` | `postgres` | Database server hostname | +| `port` | `5432` | Database server port | +| `user` | `db_app` | Application user (SELECT, INSERT, UPDATE, DELETE) | +| `migrationUser` | `db_migrator` | Migration user (CREATE, ALTER, DROP) | +| `dbAppUserPasswordSecretRef` | `` | Password of application user | +| `dbMigrationUserPasswordSecretRef` | `` | Password of migration user | +| `database` | `registry` | Database name | +| `sslMode` | `prefer` | SSL mode (disable, prefer, require, verify-full) | +| `maxOpenConns` | `10` | Maximum open connections | +| `maxIdleConns` | `2` | Maximum idle connections | +| `connMaxLifetime` | `30m` | Maximum connection lifetime | + +:::tip + +Credentials are internally configured using a pgpass file mounted as a secret. + +::: + +## Configure authentication + +You can configure authentication using the `authConfig` field in your +`MCPRegistry` resource. + +### Authentication modes + +| Mode | Description | Use case | +| ----------- | ----------------------------------------------- | ---------------------------- | +| `oauth` | Validates access tokens from identity providers | Production deployments | +| `anonymous` | No authentication required | Development and testing only | + +:::info[Secure by default] + +Configuring an authentication mode is mandatory, if you're not interested you +can set it to `anonymous`. + +::: + +### OAuth authentication + +OAuth mode validates JWT tokens from one or more identity providers. Configure +providers in the `authConfig.oauth.providers` array. + +```yaml title="registry-oauth.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRegistry +metadata: + name: registry + namespace: toolhive-system +spec: + displayName: 'Authenticated MCP Server Registry' + authConfig: + mode: oauth + oauth: + providers: + - name: kubernetes + issuerUrl: https://kubernetes.default.svc.cluster.local + jwksUrl: https://kubernetes.default.svc/openid/v1/jwks + audience: registry-server + caCertPath: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + authTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token + allowPrivateIP: true + registries: + - name: toolhive + format: toolhive + git: + repository: https://github.com/stacklok/toolhive-catalog.git + branch: main + path: pkg/catalog/toolhive/data/registry.json +``` + +### OAuth configuration fields + +| Field | Required | Default | Description | +| ----------------- | -------- | ----------------------------------------- | -------------------------------------------------- | +| `mode` | No | `oauth` | Authentication mode (`oauth` or `anonymous`) | +| `resourceUrl` | No | - | URL identifying this protected resource (RFC 9728) | +| `realm` | No | `mcp-registry` | Protection space identifier for WWW-Authenticate | +| `scopesSupported` | No | `[mcp-registry:read, mcp-registry:write]` | OAuth scopes supported by this resource | + +### Provider configuration fields + +| Field | Required | Description | +| ------------------ | -------- | ----------------------------------------------------------------------- | +| `name` | Yes | Unique identifier for this provider (for logging and monitoring) | +| `issuerUrl` | Yes | OIDC issuer URL (e.g., `https://accounts.google.com`) | +| `audience` | Yes | Expected audience claim in the access token | +| `jwksUrl` | No | JWKS endpoint URL (skips OIDC discovery if specified) | +| `clientId` | No | OAuth client ID for token introspection | +| `clientSecretFile` | No | Path to file containing the client secret | +| `caCertPath` | No | Path to CA certificate for TLS verification | +| `authTokenFile` | No | Path to token file for authenticating to OIDC/JWKS endpoints | +| `introspectionUrl` | No | Token introspection endpoint URL for opaque token validation (RFC 7662) | +| `allowPrivateIP` | No | Allow connections to private IP addresses (required for in-cluster) | + +### Kubernetes service account authentication + +For in-cluster deployments, you can configure OAuth to validate Kubernetes +service account tokens: + +```yaml title="registry-k8s-auth.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRegistry +metadata: + name: registry +spec: + authConfig: + mode: oauth + oauth: + providers: + - name: kubernetes + issuerUrl: https://kubernetes.default.svc.cluster.local + jwksUrl: https://kubernetes.default.svc/openid/v1/jwks + audience: registry-server + caCertPath: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + authTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token + allowPrivateIP: true +``` + +:::tip[Kubernetes provider settings] + +- **issuerUrl**: for most Kubernetes distributions, + `https://kubernetes.default.svc.cluster.local` is the correct value to match + the `iss` claim in Kubernetes service account tokens. +- **jwksUrl**: Specify directly to skip OIDC discovery (the Kubernetes API + server doesn't support standard discovery). +- **allowPrivateIP**: Required for in-cluster communication with the API server. + +::: + +### Multiple providers + +You can configure multiple OAuth providers to accept tokens from different +identity sources: + +```yaml title="registry-multi-provider.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRegistry +metadata: + name: registry +spec: + authConfig: + mode: oauth + oauth: + providers: + # Kubernetes service accounts (in-cluster workloads) + - name: kubernetes + issuerUrl: https://kubernetes.default.svc.cluster.local + jwksUrl: https://kubernetes.default.svc/openid/v1/jwks + audience: registry-server + caCertPath: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + authTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token + allowPrivateIP: true + # External identity provider + - name: okta + issuerUrl: https://YOUR_DOMAIN.okta.com/oauth2/default + audience: registry +``` + +The server validates tokens against each provider in order until one succeeds. + +### Anonymous authentication + +For development and testing, you can disable authentication entirely: + +```yaml title="registry-anonymous.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRegistry +metadata: + name: registry +spec: + authConfig: + mode: anonymous + registries: + - name: toolhive + format: toolhive + git: + repository: https://github.com/stacklok/toolhive-catalog.git + branch: main + path: pkg/catalog/toolhive/data/registry.json +``` + +:::danger[No access control] + +Anonymous mode provides **no access control**. Only use it in trusted +environments or when other security measures are in place. **Do not use +anonymous mode in production.** + +::: + +For detailed information about authentication configuration, including +provider-specific examples for Keycloak, Auth0, Azure AD, and Okta, see the +[Authentication configuration](./authentication.mdx) guide. + +## Customize the Registry server pod + +You can customize the Registry server pod using the `podTemplateSpec` field. +This gives you full control over the pod specification. + +```yaml {6-15} title="registry-custom-pod.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRegistry +metadata: + name: custom-registry +spec: + podTemplateSpec: + spec: + containers: + - name: registry-api # This name must be "registry-api" + resources: + limits: + cpu: '500m' + memory: '512Mi' + requests: + cpu: '100m' + memory: '128Mi' + auth: + mode: anonymous + registries: + - name: toolhive + format: toolhive + git: + repository: https://github.com/stacklok/toolhive-catalog.git + branch: main + path: pkg/catalog/toolhive/data/registry.json + syncPolicy: + interval: '30m' +``` + +:::info[Container name requirement] + +When customizing containers in `podTemplateSpec`, you must use +`name: registry-api` for the main container to ensure the operator can properly +manage the Registry server. The container name is hardcoded to avoid conflict +issues with user provided containers. Mandating a container name on the Operator +side explicitly tells the Operator it is the main registry server container and +any other containers provided by the use are sidecars/init containers. + +::: + +## Check registry status + +To check the status of your registries in a specific namespace: + +```bash +kubectl -n get mcpregistries +``` + +To check registries across all namespaces: + +```bash +kubectl get mcpregistries --all-namespaces +``` + +The status displays the phase, message, and age of each registry. + +For more details about a specific registry: + +```bash +kubectl -n describe mcpregistry +``` + +### Registry phases + +| Phase | Description | +| ------------- | -------------------------------------- | +| `Pending` | The registry is being initialized | +| `Ready` | The registry is ready and operational | +| `Syncing` | The registry is currently syncing data | +| `Failed` | The registry has encountered an error | +| `Terminating` | The registry is being deleted | + +## Next steps + +Learn how to configure authentication for the Registry server in the +[Authentication configuration](./authentication.mdx) guide. + +Configure additional registry sources and filtering options in the +[Configuration](./configuration.mdx) guide. + +Discover your deployed MCP servers automatically using the +[Kubernetes registry](./configuration.mdx#kubernetes-registry) feature. Note +that the operator automatically creates a single Kubernetes registry named +`default` - you cannot configure additional Kubernetes registries. + +## Related information + +- [Kubernetes CRD reference](../reference/crd-spec.md#apiv1alpha1mcpregistry) - + Reference for the `MCPRegistry` Custom Resource Definition (CRD) +- [Deploy the operator](../guides-k8s/deploy-operator.mdx) - Install the + ToolHive operator +- [Database configuration](./database.mdx) - Configure PostgreSQL storage + +## Troubleshooting + +
+MCPRegistry resource not creating pods + +If your `MCPRegistry` resource is created but no pods appear: + +1. Ensure you created the `MCPRegistry` resource in an allowed namespace +1. Check the operator's configuration: + +```bash +helm get values toolhive-operator -n toolhive-system +``` + +1. Check the MCPRegistry status and operator logs: + +```bash +# Check MCPRegistry status +kubectl -n describe mcpregistry + +# Check operator logs +kubectl -n toolhive-system logs -l app.kubernetes.io/name=toolhive-operator + +# Verify the operator is running +kubectl -n toolhive-system get pods -l app.kubernetes.io/name=toolhive-operator +``` + +Common causes include: + +- **Operator not running**: Ensure the ToolHive operator is deployed and running +- **RBAC issues**: Check for cluster-level permission issues +- **Resource quotas**: Check if namespace resource quotas prevent pod creation + +
+ +
+Registry stuck in Pending or Syncing phase + +If the registry is stuck in `Pending` or `Syncing` phase: + +```bash +# Check registry status +kubectl -n describe mcpregistry + +# Check registry pod logs +kubectl -n logs -l app.kubernetes.io/instance= +``` + +Common causes include: + +- **Git repository inaccessible**: Verify the repository URL is correct and + accessible +- **ConfigMap/PVC doesn't exist**: Ensure referenced resources exist in the same + namespace +- **Network policies**: Check if network policies are blocking external access +- **Invalid registry file format**: Verify the registry JSON file is valid + +
+ +
+Database connection errors + +If you see database connection errors: + +```bash +# Check registry pod logs +kubectl -n logs -l app.kubernetes.io/instance= +``` + +Common causes include: + +- **Database not reachable**: Verify database host and port are correct +- **Invalid credentials**: Check that pgpass file is properly mounted +- **SSL configuration mismatch**: Verify `sslMode` matches your database + configuration +- **Permission issues**: Ensure database users have required privileges + +
+ +
+Sync failures + +If synchronization is failing: + +```bash +# Check sync status +kubectl -n get mcpregistry -o jsonpath='{.status.syncStatus}' + +# Trigger manual sync to see immediate errors +kubectl annotate mcpregistry \ + toolhive.stacklok.dev/sync-trigger="$(date +%s)" \ + --overwrite + +# Check logs +kubectl -n logs -l app.kubernetes.io/instance= +``` + +Common causes include: + +- **Source unavailable**: Git repository, API endpoint, or file is inaccessible +- **Invalid JSON format**: Registry file contains invalid JSON +- **Format mismatch**: The `format` field doesn't match the actual data format +- **Filter too restrictive**: Filters may be excluding all servers + +
diff --git a/versioned_docs/version-1.0/toolhive/guides-registry/deployment.mdx b/versioned_docs/version-1.0/toolhive/guides-registry/deployment.mdx new file mode 100644 index 00000000..8a93e939 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-registry/deployment.mdx @@ -0,0 +1,34 @@ +--- +title: Deploy the Registry Server +description: Deployment options for the ToolHive Registry Server in Kubernetes +--- + +The Registry server can be deployed in Kubernetes using three methods. Choose +the one that fits your environment: + +| Method | Description | +| ------------------------------------------------ | --------------------------------------------------------------- | +| [ToolHive Operator](#toolhive-operator) | Manage the Registry Server lifecycle through `MCPRegistry` CRDs | +| [Helm](#helm) | Deploy using the Registry Server Helm chart | +| [Manual manifests](#manual-kubernetes-manifests) | Deploy directly using raw Kubernetes manifests | + +## ToolHive Operator + +Deploy and manage the Registry server using `MCPRegistry` custom resources. The +ToolHive Operator watches for these resources and creates the necessary +infrastructure automatically. + +See [Deploy with the ToolHive Operator](./deploy-operator.mdx) for a complete +guide. + +## Helm + +A Helm chart is available for the Registry Server. Documentation for this +deployment method is coming soon. + +## Manual Kubernetes manifests + +Deploy the Registry Server directly using raw Kubernetes manifests. This +approach gives you full control over the deployment configuration. + +See [Deploy manually](./deploy-manual.mdx) for instructions. diff --git a/versioned_docs/version-1.0/toolhive/guides-registry/index.mdx b/versioned_docs/version-1.0/toolhive/guides-registry/index.mdx new file mode 100644 index 00000000..cd771ac8 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-registry/index.mdx @@ -0,0 +1,20 @@ +--- +title: ToolHive Registry Server +description: + How-to guides for using the ToolHive Registry Server to discover and access + MCP servers and skills. +--- + +import DocCardList from '@theme/DocCardList'; + +## Introduction + +The ToolHive Registry Server is an implementation of the official +[Model Context Protocol (MCP) Registry API specification](https://github.com/modelcontextprotocol/registry/blob/main/docs/reference/api/generic-registry-api.md). +It provides a standardized REST API for discovering and accessing MCP servers +from multiple backend sources, including Kubernetes clusters, Git repositories, +API endpoints, and local files. + +## Contents + + diff --git a/versioned_docs/version-1.0/toolhive/guides-registry/intro.mdx b/versioned_docs/version-1.0/toolhive/guides-registry/intro.mdx new file mode 100644 index 00000000..b096652a --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-registry/intro.mdx @@ -0,0 +1,110 @@ +--- +title: Overview +sidebar_label: Introduction +description: + How to use the ToolHive Registry server to discover and access MCP servers and + skills +--- + +The ToolHive Registry server is a standards-compliant implementation of the MCP +Registry API specification. It provides a REST API for discovering and accessing +MCP servers from multiple backend sources. + +## Overview + +The Registry server aggregates MCP server metadata from various sources and +exposes it through a standardized API. When you start the server, it: + +1. Loads configuration from a YAML file +2. Runs database migrations automatically +3. Immediately fetches registry data from the configured sources +4. Starts background sync coordinator for automatic updates (for synced + registries) +5. Serves MCP Registry API endpoints on the configured address + +```mermaid +flowchart LR + subgraph Sources["Registry sources"] + direction LR + Managed["Managed registry"] + API["Upstream registry"] + Kubernetes["Kubernetes cluster"] + Git["Git repository"] + File["Local file"] + end + subgraph Server["Registry server"] + direction TB + Config["Configuration"] + Sync["Sync manager"] + Storage["Storage layer"] + APIHandler["API handler"] + end + subgraph Clients["Clients"] + direction LR + ToolHive["ToolHive"] + MCPClient["MCP clients"] + end + Sources -->|sync| Server + Config --> Sync + Sync --> Storage + Storage --> APIHandler + Server -->|REST API| Clients +``` + +## Features + +- **Standards-compliant**: Implements the official MCP Registry API + specification +- **Multiple registry sources**: Git repositories, API endpoints, local files, + managed registries, and Kubernetes discovery +- **Automatic synchronization**: Background sync with configurable intervals and + retry logic for Git, API, and File sources +- **Container-ready**: Designed for deployment in Kubernetes clusters, but can + be deployed anywhere +- **Flexible deployment**: Works standalone or as part of ToolHive + infrastructure +- **Production-ready**: Built-in health checks, graceful shutdown, and sync + status persistence +- **Kubernetes-aware**: Automatic discovery of MCP servers deployed via ToolHive + Operator +- **Skills registry**: Publish and discover reusable + [skills](../concepts/skills.mdx) through the extensions API + +## Registry sources + +The server supports five registry source types: + +1. **Managed Registry** - A fully-managed MCP Registry + - Ideal for private repositories + - Automatically exposes entries following upstream MCP Registry format + - Supports adding new MCP servers via `/publish` endpoint + +2. **Upstream Registry** - Sync from upstream MCP Registry APIs + - Supports federation and aggregation scenarios + - Format conversion from upstream to ToolHive format + - Does not support publishing + +3. **Kubernetes Cluster** - Automatically creates registry entries for running + workloads + - Ideal to quickly grant access to running MCP servers to knowledge workers + - Useful for bigger organizations where MCP server developers differ from + users + - Does not support publishing + +4. **Git Repository** - Clone and sync from Git repositories + - Supports branch, tag, or commit pinning + - Ideal for version-controlled registries + - Does not support publishing + +5. **Local File** - Read from filesystem + - Ideal for local development and testing + - Supports mounted volumes in containers + - Does not support publishing + +## Next steps + +- [Configure registry sources](./configuration.mdx) to set up your registry +- [Deploy the server](./deployment.mdx) in your environment +- [Manage skills](./skills.mdx) to publish and discover reusable skills +- [View the API reference](../reference/registry-api.mdx) for endpoint + documentation diff --git a/versioned_docs/version-1.0/toolhive/guides-registry/skills.mdx b/versioned_docs/version-1.0/toolhive/guides-registry/skills.mdx new file mode 100644 index 00000000..850c2e72 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-registry/skills.mdx @@ -0,0 +1,202 @@ +--- +title: Manage skills +description: + How to publish, list, retrieve, and delete skills using the ToolHive Registry + server extensions API. +--- + +The Registry server provides an extensions API for managing +[skills](../concepts/skills.mdx). This guide covers the full lifecycle: +publishing, listing, retrieving, and deleting skills. + +## Prerequisites + +- A running Registry server with at least one **managed** registry configured + (skills can only be published to managed registries) +- `curl` or another HTTP client +- If authentication is enabled, a valid bearer token (see + [Authentication](./authentication.mdx)) + +## API base path + +All skills endpoints use the following base path: + +```text +/{registryName}/v0.1/x/dev.toolhive/skills +``` + +Replace `{registryName}` with the `name` of the registry as defined in your +[configuration file](./configuration.mdx) (for example, `my-registry` if your +config has `registries: [{name: my-registry, ...}]`). + +## Publish a skill + +To publish a new skill version, send a `POST` request with the skill metadata: + +```bash title="Publish a skill" +curl -X POST \ + https://registry.example.com/my-registry/v0.1/x/dev.toolhive/skills \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer " \ + -d '{ + "namespace": "io.github.acme", + "name": "code-review", + "description": "Performs structured code reviews using best practices", + "version": "1.0.0", + "status": "active", + "title": "Code Review Assistant", + "license": "Apache-2.0", + "packages": [ + { + "registryType": "git", + "url": "https://github.com/acme/skills", + "ref": "v1.0.0", + "subfolder": "code-review" + } + ], + "repository": { + "url": "https://github.com/acme/skills", + "type": "git" + } + }' +``` + +**Required fields:** `namespace`, `name`, `description`, `version` + +A successful response returns `201 Created` with the published skill. If the +version already exists, the server returns `409 Conflict`. + +The `status` field accepts `active`, `deprecated`, or `archived` (case +insensitive). The server normalizes status values to uppercase internally. + +### Versioning behavior + +When you publish a new version, the registry compares it against the current +latest version. If the new version is newer, the latest pointer updates +automatically. Publishing an older version (for example, backfilling `0.9.0` +after `1.0.0` exists) does not change the latest pointer. + +## List skills + +To list skills in a registry: + +```bash title="List all skills" +curl https://registry.example.com/my-registry/v0.1/x/dev.toolhive/skills +``` + +### Query parameters + +| Parameter | Type | Default | Description | +| --------- | ------ | ------- | -------------------------------------------------- | +| `search` | string | - | Filter by name or description substring | +| `status` | string | - | Filter by status (comma-separated, e.g., `active`) | +| `limit` | int | 50 | Maximum results per page (1-100) | +| `cursor` | string | - | Pagination cursor from a previous response | + +### Search example + +```bash title="Search for skills by keyword" +curl "https://registry.example.com/my-registry/v0.1/x/dev.toolhive/skills?search=review&limit=10" +``` + +### Response format + +```json +{ + "skills": [ + { + "namespace": "io.github.acme", + "name": "code-review", + "description": "Performs structured code reviews using best practices", + "version": "1.0.0", + "status": "ACTIVE", + "title": "Code Review Assistant", + "license": "Apache-2.0", + "packages": [ + { + "registryType": "git", + "url": "https://github.com/acme/skills", + "ref": "v1.0.0", + "subfolder": "code-review" + } + ], + "repository": { + "url": "https://github.com/acme/skills", + "type": "git" + } + } + ], + "metadata": { + "count": 1, + "nextCursor": "" + } +} +``` + +Use the `nextCursor` value to fetch the next page of results. An empty +`nextCursor` indicates there are no more pages. + +## Retrieve a skill + +### Get the latest version + +```bash title="Get the latest version of a skill" +curl https://registry.example.com/my-registry/v0.1/x/dev.toolhive/skills/io.github.acme/code-review +``` + +### Get a specific version + +```bash title="Get a specific version" +curl https://registry.example.com/my-registry/v0.1/x/dev.toolhive/skills/io.github.acme/code-review/versions/1.0.0 +``` + +### List all versions + +```bash title="List all versions of a skill" +curl https://registry.example.com/my-registry/v0.1/x/dev.toolhive/skills/io.github.acme/code-review/versions +``` + +## Delete a skill version + +To delete a specific version: + +```bash title="Delete a skill version" +curl -X DELETE \ + https://registry.example.com/my-registry/v0.1/x/dev.toolhive/skills/io.github.acme/code-review/versions/1.0.0 \ + -H "Authorization: Bearer " +``` + +A successful delete returns `204 No Content`. Deleting a non-existent version +returns `404 Not Found`. + +:::warning + +Deleting a skill version is permanent. If the deleted version was the latest, +the latest pointer is not automatically reassigned to a previous version. + +::: + +## Error responses + +The API returns standard HTTP status codes: + +| Code | Meaning | +| ---- | ------------------------------------------------------------- | +| 400 | Invalid request (missing required fields, invalid parameters) | +| 401 | Authentication required | +| 403 | Registry is not a managed registry (read-only registries) | +| 404 | Registry, skill, or version not found | +| 409 | Version already exists | +| 500 | Internal server error | + +## What's next + +Skill installation via agent clients (such as the ToolHive CLI or IDE +extensions) is planned for a future release. For now, the registry serves as a +discovery and distribution layer. + +- [Understanding skills](../concepts/skills.mdx) for background on the skills + model +- [Registry server introduction](./intro.mdx) for an overview of the Registry + server +- [Authentication](./authentication.mdx) for configuring API access diff --git a/versioned_docs/version-1.0/toolhive/guides-registry/telemetry-metrics.mdx b/versioned_docs/version-1.0/toolhive/guides-registry/telemetry-metrics.mdx new file mode 100644 index 00000000..eb7d3e73 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-registry/telemetry-metrics.mdx @@ -0,0 +1,185 @@ +--- +title: Telemetry and metrics +description: + How to enable OpenTelemetry metrics and distributed tracing for the ToolHive + Registry Server +--- + +The ToolHive Registry Server provides comprehensive observability through +OpenTelemetry (OTel), supporting both distributed tracing and metrics collection +via OTLP exporters. This enables you to monitor system behavior, diagnose +issues, and improve performance. + +## Architecture overview + +The Registry Server exports telemetry data (traces and metrics) via OTLP HTTP to +an OpenTelemetry Collector, which can then forward to various backends: + +```mermaid +flowchart TB + subgraph server["Registry Server"] + http["HTTP requests"] + sync["Sync metrics"] + registry["Registry metrics"] + + http & sync & registry --> otlp["OTLP HTTP"] + end + + otlp --> collector["OTel Collector"] + + collector --> tempo["Tempo"] + collector --> prometheus["Prometheus"] + collector --> grafana["Grafana"] +``` + +## Configuration + +Add telemetry configuration to your Registry Server configuration file: + +```yaml title="config.yaml" +telemetry: + enabled: true + serviceName: thv-registry-api + serviceVersion: '1.0.0' + endpoint: otel-collector:4318 + insecure: true + tracing: + enabled: true + sampling: 0.05 + metrics: + enabled: true +``` + +### Configuration options + +| Option | Type | Default | Description | +| ------------------ | ------ | ------------------ | --------------------------------- | +| `enabled` | bool | `false` | Enable or disable all telemetry | +| `serviceName` | string | `thv-registry-api` | Service name in telemetry data | +| `serviceVersion` | string | `"unknown"` | Service version in telemetry data | +| `endpoint` | string | `localhost:4318` | OTLP HTTP endpoint (host:port) | +| `insecure` | bool | `false` | Use insecure connection (no TLS) | +| `tracing.enabled` | bool | `false` | Enable distributed tracing | +| `tracing.sampling` | float | `0.05` | Trace sampling ratio (0.0 to 1.0) | +| `metrics.enabled` | bool | `false` | Enable metrics collection | + +:::note + +The `endpoint` is provided as a hostname and optional port, without a scheme or +path (e.g., use `api.honeycomb.io` or `api.honeycomb.io:443`, not +`https://api.honeycomb.io`). The server automatically uses HTTPS unless +`insecure: true` is specified. + +::: + +## Metrics + +The Registry Server exposes metrics prefixed with `thv_reg_srv_` for easy +identification. + +### Available metrics + +| Metric | Type | Labels | Description | +| ------------------------------------------- | ------------- | -------------------------------- | ------------------------------ | +| `thv_reg_srv_http_request_duration_seconds` | Histogram | `method`, `route`, `status_code` | Duration of HTTP requests | +| `thv_reg_srv_http_requests_total` | Counter | `method`, `route`, `status_code` | Total number of HTTP requests | +| `thv_reg_srv_http_active_requests` | UpDownCounter | - | Number of in-flight requests | +| `thv_reg_srv_servers_total` | Gauge | `registry` | Number of servers per registry | +| `thv_reg_srv_sync_duration_seconds` | Histogram | `registry`, `success` | Duration of sync operations | + +### Histogram buckets + +The metrics use the following histogram bucket boundaries: + +- **HTTP request duration**: 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, + 5, 10 seconds +- **Sync duration**: 0.1, 0.5, 1, 2.5, 5, 10, 30, 60, 120, 300 seconds + +## Distributed tracing + +The Registry Server implements distributed tracing across two layers: HTTP +requests and service operations. + +### Trace hierarchy + +Traces follow a parent-child hierarchy that shows the complete request flow: + +```text +HTTP Request Span (root) +└── Service Span (child) + └── Database operations with db.system=postgresql +``` + +:::note + +Background sync operations are monitored through metrics (see the +`thv_reg_srv_sync_duration_seconds` metric above) rather than distributed +traces, as they are internal operations without incoming request context. + +::: + +### HTTP layer spans + +All HTTP requests (except health and readiness endpoints) are traced with the +following attributes: + +| Attribute | Type | Description | +| --------------------------- | ------ | -------------------------------------------- | +| `http.request.method` | string | HTTP method (GET, POST, etc.) | +| `http.route` | string | Route pattern (e.g., `/v0.1/servers/{name}`) | +| `url.path` | string | Actual URL path | +| `user_agent.original` | string | Client user agent (truncated to 256 chars) | +| `http.response.status_code` | int | Response status code | + +### Service layer spans + +Database service operations include these attributes: + +| Attribute | Type | Description | +| ----------------------- | ------ | --------------------------------- | +| `registry.name` | string | Name of the registry | +| `server.name` | string | Name of the server | +| `server.version` | string | Version of the server | +| `pagination.limit` | int | Page size limit | +| `pagination.has_cursor` | bool | Whether pagination cursor is used | +| `result.count` | int | Number of results returned | + +### Context propagation + +The Registry Server supports W3C Trace Context propagation. Incoming requests +with `traceparent` headers have their trace context extracted and used as the +parent for all child spans, enabling distributed tracing across multiple +services. + +## Sampling strategies + +Adjust sampling rates based on your environment and traffic volume: + +| Environment | Sampling rate | Use case | +| ----------- | ------------- | -------------------------------------------- | +| Development | `1.0` | Capture all traces for debugging | +| Staging | `0.1` | 10% sampling for testing | +| Production | `0.01 - 0.05` | 1-5% sampling to balance cost and visibility | + +:::tip + +Start with a higher sampling rate and reduce it as you understand your traffic +patterns. For high-traffic production environments, even 1% sampling provides +sufficient data for identifying issues. + +::: + +## Excluded endpoints + +The `/health` and `/readiness` endpoints are intentionally excluded from +tracing. + +These endpoints generate a high volume of nearly identical spans that provide +minimal diagnostic value while significantly increasing storage costs. HTTP +metrics still capture latency and error rates for these endpoints. + +## Next steps + +- [Configure the Registry Server](./configuration.mdx) with your registries +- [Deploy the server](./deployment.mdx) to your environment +- [Configure authentication](./authentication.mdx) for secure access diff --git a/versioned_docs/version-1.0/toolhive/guides-ui/cli-access.mdx b/versioned_docs/version-1.0/toolhive/guides-ui/cli-access.mdx new file mode 100644 index 00000000..0033e23e --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-ui/cli-access.mdx @@ -0,0 +1,140 @@ +--- +title: Access the CLI from ToolHive UI +description: + How to use the ToolHive CLI when using the ToolHive UI application for + advanced features and terminal-based workflows. +--- + +ToolHive UI includes the CLI for advanced users who want terminal access or +features not yet available in the graphical interface. ToolHive UI automatically +installs and manages the CLI, so you don't need to install it separately. + +## Why use the CLI with ToolHive UI? + +While the ToolHive UI covers most common tasks, you might want to use the CLI +for: + +- **Advanced features**: Some features are available in the CLI before they're + added to the graphical interface +- **Scripting and automation**: Integrate MCP server management into local + scripts or automated workflows +- **Personal preference**: If you prefer working in a terminal for certain + tasks, the CLI is available without a separate installation + +## Use CLI commands + +After ToolHive UI installation, you can use the CLI from your terminal: + +1. Open a new terminal window to pick up the PATH changes. + +1. Verify the CLI is available: + + ```bash + thv version + ``` + +1. Run any CLI command: + + ```bash + thv list # List running MCP servers + thv registry list # Browse available servers + thv --help # View all commands + ``` + +For detailed command reference, see the [CLI guides](../guides-cli/index.mdx) +and [command reference](../reference/cli/thv.md). + +## How ToolHive UI manages the CLI + +When you install ToolHive UI, it automatically: + +1. **Creates a symlink** to its bundled CLI binary: + - macOS/Linux: `~/.toolhive/bin/thv` + - Windows: `%LOCALAPPDATA%\ToolHive\bin\thv.exe` + +1. **Configures your PATH** by adding entries to your shell configuration files + (`.bashrc`, `.zshrc`, `config.fish`, or the Windows User PATH) + +This ensures the CLI version always matches the ToolHive UI version, preventing +compatibility issues with the API. + +:::note + +If you have a standalone CLI installed (via Homebrew, WinGet, or manually), it +will show a conflict error. See +[CLI conflict resolution](../guides-cli/install.mdx#cli-conflict-resolution) for +details. + +::: + +## The Settings > CLI page + +ToolHive UI includes a dedicated settings page to manage the CLI installation. +Access it from **Settings** (gear icon) > **CLI**. + +The page displays: + +- CLI installation status and version +- Symlink location and target path +- Shell configuration status + +Use the **Reinstall** button if the CLI becomes unavailable or the symlink +breaks (for example, after moving the ToolHive UI application). + +## Troubleshooting + +
+CLI not found in terminal + +If `thv` is not recognized after installing ToolHive UI: + +1. **Open a new terminal window**: The PATH changes only take effect in new + terminal sessions. + +1. **Check the Settings > CLI page**: Verify that the PATH Configuration shows + "Valid" status. + +1. **Manually source your shell configuration**: + + ```bash + # Bash + source ~/.bashrc + + # Zsh + source ~/.zshrc + + # Fish + source ~/.config/fish/config.fish + ``` + +1. **Reinstall the CLI**: Go to Settings > CLI and click **Reinstall**. + +
+ +
+Broken symlink after moving ToolHive UI + +If you move the ToolHive UI application to a different location, the CLI symlink +may break. To fix this: + +1. Open ToolHive UI from its new location. +1. Go to Settings > CLI. +1. Click **Reinstall** to create a new symlink pointing to the correct location. + +
+ +
+CLI conflict error when running thv + +If you see "CLI conflict detected", you have both ToolHive UI and a standalone +CLI installed. See +[CLI conflict resolution](../guides-cli/install.mdx#cli-conflict-resolution) for +the error message and resolution steps. + +
+ +## Related information + +- [CLI guides](../guides-cli/index.mdx) +- [CLI command reference](../reference/cli/thv.md) +- [CLI conflict resolution](../guides-cli/install.mdx#cli-conflict-resolution) diff --git a/versioned_docs/version-1.0/toolhive/guides-ui/client-configuration.mdx b/versioned_docs/version-1.0/toolhive/guides-ui/client-configuration.mdx new file mode 100644 index 00000000..c0d539b9 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-ui/client-configuration.mdx @@ -0,0 +1,85 @@ +--- +title: Client configuration +description: How to connect ToolHive to AI clients using the UI. +--- + +import ClientIntro from '../_partials/_client-config-intro.mdx'; + + + +## Manage clients + +ToolHive automatically discovers supported AI clients installed on your system. +To choose which clients ToolHive configures, open the **MCP Servers** page and +click **Manage Clients**. This opens a dialog that lists detected clients with +on/off toggles. Discovery is dynamic: if you install a new client, ToolHive +updates the list. + +:::tip[Using groups?] + +If you organize your MCP servers into groups, you can configure client access +per group. This lets you control which servers each client can access. See +[Organize servers into groups](./group-management.mdx) for details. + +::: + +### Connect a client + +To connect a client to ToolHive: + +1. Go to **MCP Servers** and click **Manage Clients**. +2. Turn on the toggle for the client you want to connect. +3. Click **Save** to apply your changes. + +When you connect a client and save, ToolHive configures it to use your running +MCP servers. Any new servers you start are also added automatically. When you +stop or remove an MCP server, ToolHive updates the client's configuration to +remove the server. + +### Disconnect a client + +To disconnect, open **Manage Clients**, turn off the client's toggle, then click +**Save**. ToolHive removes the MCP server configurations from that client. The +client can no longer use ToolHive-managed MCP servers. + +### Save your changes + +Changes you make in **Manage Clients** are not applied until you click **Save**. +If you close the dialog or click **Cancel** without saving, your toggle changes +are discarded. + +## Why don't I see my client? + +If you don't see your client in **Manage Clients**, ToolHive might not support +it, or its configuration file might not be in a location ToolHive recognizes. + +For a list of supported clients and how ToolHive detects them, see the +[client compatibility reference](../reference/client-compatibility.mdx). + +## Why do I still see a client I uninstalled? + +Many clients leave behind configuration files even after you uninstall them. +ToolHive detects these files and continues to show the client in the **Manage +Clients** dialog. If you want to remove the client from ToolHive, delete its +configuration files manually. + +## Manual client configuration + +For clients that ToolHive doesn't support directly, you can still configure them +to connect to ToolHive-managed MCP servers using the SSE (Server-Sent Events) or +Streamable HTTP protocol. + +To do this, you need the URL of the MCP server you want to connect to. You can +get this URL from the **MCP Servers** page in ToolHive. Click the menu (︙) on +the MCP server card to copy the URL. + +You can then configure your client to use this URL. The exact steps depend on +your client or library, so refer to its documentation for details. See the +[client configuration reference](../reference/client-compatibility.mdx#manual-configuration) +for some common examples. + +## Related information + +- [Client compatibility](../reference/client-compatibility.mdx) +- [Run MCP servers](./run-mcp-servers.mdx) +- [Organize servers into groups](./group-management.mdx) diff --git a/versioned_docs/version-1.0/toolhive/guides-ui/customize-tools.mdx b/versioned_docs/version-1.0/toolhive/guides-ui/customize-tools.mdx new file mode 100644 index 00000000..220b5bd7 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-ui/customize-tools.mdx @@ -0,0 +1,201 @@ +--- +title: Customize tools +description: + Select which tools from an MCP server are available to your AI clients using + the ToolHive UI. +--- + +ToolHive lets you customize which tools from a running MCP server are available +to your AI clients. You can enable or disable individual tools, and customize +their names and descriptions. This gives you fine-grained control over the +capabilities exposed by each server, helping you create focused tool sets and +reduce complexity for your AI workflows. + +## Why customize tools? + +Customizing tools helps you: + +- **Reduce complexity**: Hide tools that aren't relevant to your workflow, + making it easier for AI clients to select the right tool +- **Improve performance**: Fewer tools mean faster tool selection and reduced + token usage in AI interactions +- **Enhance security**: Limit exposure to only the tools you need, reducing the + risk of unintended actions +- **Create focused environments**: Tailor tool availability to specific tasks or + projects +- **Clarify tool purpose**: Edit tool names and descriptions to make them more + understandable for your specific use case + +:::info[Registry match required] + +Tool customization is only available for MCP servers that match an entry in the +registry. This includes: + +- Servers installed directly from the registry +- Custom servers with a docker image or URL that matches a registry entry + +Custom servers that don't match any registry entry do not support this feature. + +::: + +## Access tool customization + +To customize tools for a running MCP server: + +1. Open the **MCP Servers** page. +1. Find the server you want to customize and click the menu (︙) on its card. +1. Select **Customize Tools** from the dropdown menu. + +The customize tools page displays all available tools from the MCP server, each +with a toggle switch and description. + +:::note[Server must be running] + +The MCP server must be running to view and customize tools. If the server is +stopped when you access the customize tools page, you'll see a message prompting +you to start the server first. + +::: + +## Enable or disable tools + +On the customize tools page: + +1. Review the list of available tools and their descriptions. +1. Use the toggle switches to enable or disable individual tools: + - **Green (enabled)**: The tool is available to AI clients + - **Gray (disabled)**: The tool is hidden from AI clients +1. Click **Apply** to save your changes. + +The ToolHive proxy filters out disabled tools when AI clients connect to the +server. Clients only see and can only call the tools you've enabled. + +:::warning[At least one tool required] + +You must keep at least one tool enabled. ToolHive prevents you from disabling +all tools on a server, as this would make the server non-functional. + +::: + +## Edit tool names and descriptions + +You can customize the name and description of any tool to make it more suitable +for your workflow. This is useful when the default tool name or description is +unclear, or when you want to adapt the tool's presentation to your specific use +case. + +To edit a tool: + +1. On the customize tools page, find the tool you want to edit. +1. Click the **Edit** button next to the tool. +1. In the dialog that opens: + - Edit the **Tool** field to change the tool's name. + - Edit the **Description** field to customize the tool's description. + - The original values are shown as helper text for reference. + - Leave either field empty to reset it to the original value. +1. Click **Save** to apply your changes, or **Cancel** to discard them. + +Tools with custom names or descriptions are marked with a wrench icon to +indicate they have custom overrides applied. The icon color indicates the +status: + +- **Orange**: The tool has unsaved custom overrides +- **Primary color**: The tool has saved custom overrides + +Your customizations are preserved even if you disable and re-enable the tool. + +:::tip[Unsaved custom overrides] + +If you customize a tool's name or description but haven't clicked **Apply** on +the main page, a tooltip appears indicating "This tool has unsaved custom +overrides". Click **Apply** to save both your tool selection changes and custom +overrides. + +::: + +## How filtering works + +When you customize tools: + +1. You toggle tools on or off using the switches and optionally edit their names + and descriptions. +1. After clicking **Apply**, your settings are saved to the server + configuration. +1. The ToolHive proxy intercepts tool discovery requests from AI clients. +1. Only enabled tools appear in the client's tool list, with your custom names + and descriptions if you've set them. +1. If a client attempts to call a disabled tool directly, the proxy blocks the + request. + +This filtering happens transparently without requiring any changes to your AI +client configuration. Your clients automatically see the updated tool list with +customized names and descriptions the next time they connect. + +:::tip + +If you change tool settings while a client is connected, you may need to restart +the client session to see the updated tool list. Some clients cache tool lists +and don't automatically refresh. + +::: + +## Example workflows + +### Focus on specific GitHub operations + +If you're using the GitHub MCP server but only need pull request tools: + +1. Open **Customize Tools** for the GitHub server. +1. Disable all tools except those related to pull requests (like + `create_pull_request`, `get_pull_request`, `list_pull_requests`). +1. Your AI clients now see only pull request tools, making it easier to work + with GitHub PRs without distraction from issue, branch, or repository tools. + +### Create environment-specific tool sets + +If you're running the same MCP server in different groups for different +environments: + +1. Copy the server to multiple groups (see + [Organize servers into groups](./group-management.mdx)). +1. Customize tools in each group to match the environment's needs. +1. For example, enable only read-only tools in a production group, while + allowing all tools in a development group. + +### Clarify technical tool names + +If an MCP server exposes tools with technical or unclear names: + +1. Open **Customize Tools** for the server. +1. Click **Edit** on tools with unclear names. +1. Change the tool name to something more descriptive (for example, rename + `list_commits_test` to `List Commits`). +1. Update the description to clarify what the tool does in your context. +1. Click **Save**, then **Apply**. +1. Your AI clients now see clearer, more intuitive tool names and descriptions. + +## Reset to defaults + +To restore all tools to their default enabled state and remove custom overrides: + +**To reset an individual tool's name or description:** + +1. Click the **Edit** button next to the tool. +1. Clear the **Tool** and **Description** fields (leave them empty). +1. Click **Save**. +1. Click **Apply** to save your changes. + +The tool name and description revert to their original values, and the wrench +icon is removed. + +**To enable all tools:** + +1. Open the customize tools page for the server. +1. Manually enable all tools using the toggle switches. +1. Click **Apply** to save your changes. + +## Related information + +- [Run MCP servers](./run-mcp-servers.mdx) +- [Organize servers into groups](./group-management.mdx) +- [Client configuration](./client-configuration.mdx) diff --git a/versioned_docs/version-1.0/toolhive/guides-ui/group-management.mdx b/versioned_docs/version-1.0/toolhive/guides-ui/group-management.mdx new file mode 100644 index 00000000..dca3454a --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-ui/group-management.mdx @@ -0,0 +1,158 @@ +--- +title: Organize servers into groups +description: + How to organize MCP servers into groups and configure client access in the + ToolHive UI. +--- + +This guide explains how to organize your MCP servers into groups and configure +which groups your MCP clients can access. + +:::tip[New to groups?] + +If you're not sure whether groups are right for your use case, see +[Organizing MCP servers with groups](../concepts/groups.mdx) for an overview of +when and why to use them. + +::: + +:::info[What's the default behavior?] + +The `default` group always exists and cannot be deleted. When you add an MCP +server, the `default` group is preselected in the group field unless you choose +a different group. + +::: + +## View and navigate groups + +The **MCP Servers** page includes a sidebar on the left that lists all your +groups. The active group is highlighted. + +To view servers in a different group, click on the group name in the sidebar. +The **MCP Servers** page updates to show only the servers in that group. + +## Create a group + +To create a new group: + +1. Open the **MCP Servers** page. +2. Click **Add a group** in the sidebar. +3. Enter a unique name for the group. +4. Click **Create**. + +:::note + +Group names must be unique. If you try to create a group with a name that +already exists, you'll see an error message. + +::: + +## Assign servers to groups + +When you install or configure an MCP server, you assign it to a group. All +server types (registry, custom local, and remote) include a **Group** field in +their configuration forms. + +### Assign a server during installation + +When installing a new MCP server from the registry or adding a custom server: + +1. Fill in the server configuration. +2. In the **Group** field, select the group where you want to add the server. +3. Click **Install server**. + +### Copy a server to a different group + +You can copy a server to a different group: + +1. On the **MCP Servers** page, find the server you want to copy. +2. Click the menu (︙) on the server card. +3. Select **Copy server to a group**. +4. Choose the destination group. +5. Click **Copy**. + +This creates a complete copy of the server in the destination group, preserving +all configuration including secrets, environment variables, and storage volumes. +The original server remains in its current group. + +## Manage client access per group + +You can control which AI clients have access to servers in each group. This lets +you configure different tool sets for different clients or environments. + +To manage client access for a group: + +1. Navigate to the group by clicking its name in the sidebar. +2. Click **Manage Clients**. +3. Toggle the switches to enable or disable clients for this group. +4. Click **Save**. + +When you enable a client for a group, ToolHive registers that client to access +all servers in the group. When you disable a client for a group, ToolHive +unregisters the client from that group. + +:::tip + +You can enable the same client for multiple groups. The client will have access +to servers from all groups where it's enabled. + +::: + +## Delete a group + +To delete a group: + +1. Navigate to the group by clicking its name in the sidebar. +2. Click the group actions menu (⋮) in the header. +3. Select **Delete group**. +4. Confirm the deletion in the dialog. + +:::warning + +Deleting a group permanently removes all servers in that group. This action +cannot be undone. If you want to preserve the servers, copy them to another +group before deleting. + +::: + +You cannot delete the `default` group. + +## Example workflows + +### Separate development and production servers + +1. Create two groups: "development" and "production". +2. Install your MCP servers, assigning development tools to the "development" + group and production tools to the "production" group. +3. Configure your primary AI client (like GitHub Copilot) to access only the + "development" group for day-to-day coding work. +4. Use a different client or manually configure clients to access the + "production" group when needed. + +### Create environment-specific tool sets + +If you're running the same MCP server in different groups for different +environments: + +1. Copy the server to multiple groups (see + [Copy a server to a different group](#copy-a-server-to-a-different-group)). +2. Customize tools in each group to match the environment's needs (see + [Customize tools](./customize-tools.mdx)). +3. For example, enable only read-only tools in a production group, while + allowing all tools in a development group. + +### Project-based organization + +1. Create groups for each project: "webapp-frontend", "webapp-backend", and + "mobile-app". +2. Install project-specific MCP servers in each group (for example, React tools + in "webapp-frontend", database tools in "webapp-backend"). +3. Configure different AI clients or workspaces to access the appropriate groups + for each project. + +## Related information + +- [Run MCP servers](./run-mcp-servers.mdx) +- [Client configuration](./client-configuration.mdx) +- [Secrets management](./secrets-management.mdx) diff --git a/versioned_docs/version-1.0/toolhive/guides-ui/index.mdx b/versioned_docs/version-1.0/toolhive/guides-ui/index.mdx new file mode 100644 index 00000000..11b55324 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-ui/index.mdx @@ -0,0 +1,46 @@ +--- +title: Using ToolHive +description: How-to guides for using the ToolHive UI to run and manage MCP servers. +--- + +import DocCardList from '@theme/DocCardList'; + +import useBaseUrl from '@docusaurus/useBaseUrl'; +import ThemedImage from '@theme/ThemedImage'; + +## Introduction + +The ToolHive UI is a desktop application that makes it easy to run and manage +Model Context Protocol (MCP) servers on your local machine. It provides a +user-friendly interface to discover, deploy, and manage MCP servers with +security and ease of use built in. + +It's designed for anyone who wants to run MCP servers without needing to +understand the complexities of Docker or command-line tools. Whether you're a +developer, researcher, or just curious about MCP servers, ToolHive provides a +simple way to get started. + + +
+ +We strive to make ToolHive intuitive and easy to use. If we've missed the mark +on something, [let us know](https://discord.gg/stacklok)! + +:::tip[Advanced users] + +ToolHive UI includes and manages the CLI automatically for terminal access and +advanced features. See [Access the CLI from ToolHive UI](./cli-access.mdx) for +details. + +::: + +## Contents + + diff --git a/versioned_docs/version-1.0/toolhive/guides-ui/install.mdx b/versioned_docs/version-1.0/toolhive/guides-ui/install.mdx new file mode 100644 index 00000000..5fdaa1e7 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-ui/install.mdx @@ -0,0 +1,269 @@ +--- +title: Install ToolHive +description: How to install the ToolHive UI application. +--- + +This guide walks you through installing, upgrading, and managing the ToolHive +desktop application. + +![Latest version](https://img.shields.io/github/v/release/stacklok/toolhive-studio?style=for-the-badge&label=Latest%20version&color=bddfc2) +![Release date](https://img.shields.io/github/release-date/stacklok/toolhive-studio?display_date=published_at&style=for-the-badge&label=Released&color=bddfc2) + +## Prerequisites + +Before installing ToolHive, make sure your system meets these requirements: + +- **Operating systems**: + - macOS (Apple silicon or Intel) + - Windows 10/11 (x64) + - Linux (x86_64/amd64) +- **Container runtime**: + - Docker / Docker Desktop + - Podman / Podman Desktop + - Colima with Docker runtime + - Rancher Desktop with the dockerd/moby runtime (experimental) + +ToolHive requires minimal CPU, memory, and disk space. The exact requirements +depend on how many MCP servers you run and the resources they use. + +## Install ToolHive + +Select your operating system to see the installation instructions. + + + + +Download the latest ToolHive installer for +[Apple silicon](https://github.com/stacklok/toolhive-studio/releases/latest/download/ToolHive-arm64.dmg) +or +[Intel-based](https://github.com/stacklok/toolhive-studio/releases/latest/download/ToolHive-x64.dmg) +Macs and open the DMG file. + +Copy the ToolHive app to your Applications folder. You can then open it from +your Applications folder, Launchpad, or using Spotlight search. + + + + +Download the latest +[ToolHive installer](https://github.com/stacklok/toolhive-studio/releases/latest/download/ToolHive.Setup.exe) +and run the setup executable. + +After installation, you can find ToolHive in your Start menu or on your desktop. + +:::info[Important] + +The first time you run ToolHive, you may be prompted to allow firewall access. +If you don't allow this, ToolHive won't be able to run MCP servers. + +::: + + + + +1. Download the appropriate RPM or DEB package for your distribution from the + [ToolHive UI releases page](https://github.com/stacklok/toolhive-studio/releases/latest) + +2. Use your package manager to install the downloaded package: + - For RPM-based distributions (like Fedora or Red Hat Enterprise Linux): + + ```bash + sudo rpm -i ToolHive--1.x86_64.rpm + ``` + + - For DEB-based distributions (like Ubuntu or Debian): + + ```bash + sudo dpkg -i toolhive__amd64.deb + ``` + +For other Linux distributions, download the +[binary tarball](https://github.com/stacklok/toolhive-studio/releases/latest/download/toolhive-studio-linux-x64.tar.gz) +and extract it, then run the `ToolHive` binary directly. + + + + +## System tray icon + +When you close the ToolHive application window, it continues running in the +background so your MCP servers remain available. ToolHive installs a system tray +icon for quick access. You can use it to: + +- Enable or disable **Start on login** +- Show or hide the ToolHive application window +- Quit ToolHive, which stops all running MCP servers + +## Application settings + +Open the ToolHive settings screen from the gear icon (⚙️) in the application +window. The settings screen allows you to configure various options: + +- **Display theme**: Choose between light and dark themes for the application. + ToolHive matches your system theme by default. +- **Start on login**: Automatically start ToolHive when you log in to your + system. MCP servers that were running when you quit ToolHive are restarted + automatically. +- **Error reporting**: Enable or disable error reporting and telemetry data + collection. +- **Skip quit confirmation**: Skip the MCP server shutdown confirmation dialog + when quitting ToolHive. + +From the settings screen, you can also view version information and download the +application log file for troubleshooting. + +## Upgrade ToolHive + +ToolHive automatically checks for updates. When a new version is available, +you'll see a notification in the application. During the upgrade, ToolHive stops +all running MCP servers, updates the application, and then restarts itself and +the MCP servers. + +You can also manually install updates by downloading the latest installer for +your operating system from the +[ToolHive UI releases page](https://github.com/stacklok/toolhive-studio/releases/latest) +and running it. The installer will upgrade your existing ToolHive installation +to the latest version. See the [Install ToolHive](#install-toolhive) section for +direct download links. + +## File locations + +ToolHive stores its configuration and data files in several locations depending +on your operating system: + + + + +- The `~/Library/Application Support/ToolHive` directory contains: + - Configuration files and application data for the ToolHive UI + - MCP server logs and configurations (`logs/` and `runconfigs/` directories) + - Your encrypted secrets store (`secrets_encrypted` file) + - ToolHive CLI/API configuration file (`config.yaml`) +- The main UI application log is located at `~/Library/Logs/ToolHive/main.log` + +Since macOS is not case sensitive, the `~/Library/Application Support/ToolHive` +directory is shared by the UI and CLI if you have both installed. + + + + +- The `%LOCALAPPDATA%\ToolHive` directory contains: + - Application executables and installation logs + - MCP server logs and configurations (`logs/` and `runconfigs/` directories) + - Your encrypted secrets store (`secrets_encrypted` file) + - ToolHive CLI/API configuration file (`config.yaml`) +- The `%APPDATA%\ToolHive` directory contains: + - Configuration files and application data for the ToolHive UI +- The main UI application log is located at `%APPDATA%\ToolHive\logs\main.log` + +Since Windows is not case sensitive, the `%LOCALAPPDATA%\ToolHive` directory is +shared by the UI and CLI if you have both installed. + + + + +- The `~/.config/ToolHive` directory contains: + - Configuration files and application data for the ToolHive UI +- The `~/.config/toolhive` directory contains (note the case sensitivity): + - MCP server logs and configurations (`logs/` and `runconfigs/` directories) + - Your encrypted secrets store (`secrets_encrypted` file) + - ToolHive CLI/API configuration file (`config.yaml`) +- The main UI application log is located at `~/.config/ToolHive/logs/main.log` + +Since Linux is case sensitive, the `~/.config/ToolHive` and `~/.config/toolhive` +directories are separate. However, the ToolHive UI and CLI share the same +configuration file and secrets store to support coexistence. + + + + +You can also download the application log file from the **Settings** screen (⚙️) +in the ToolHive UI. + +## Telemetry and error reporting + +ToolHive uses [Sentry](https://sentry.io/welcome/) for error tracking and +performance monitoring to help us identify and fix issues, improve stability, +and enhance the user experience. This telemetry is enabled by default. You can +disable this by turning off the **Error reporting** option in the settings +screen (⚙️) if you prefer not to share this data. + +ToolHive collects the following information: + +- Error reports and crash logs +- Performance metrics +- Usage patterns + +This data is anonymized and does not include any personally identifiable +information. It helps us understand how ToolHive is used and identify areas for +improvement. Review the +[Stacklok privacy policy](https://www.iubenda.com/privacy-policy/29074746) and +[Terms of Service](https://stacklok.com/terms-of-service) for more details. + +## Next steps + +Now that you have ToolHive installed, you can start using it to run and manage +MCP servers. See [Run MCP servers](./run-mcp-servers.mdx) to get started. + +:::tip[CLI access for advanced users] + +ToolHive UI includes the CLI for terminal access and advanced features. See +[Access the CLI from ToolHive UI](./cli-access.mdx) to learn more. + +::: + +## Related information + +- Quickstart: [Getting started with the ToolHive UI](./quickstart.mdx) +- [Client configuration](./client-configuration.mdx) +- [Secrets management](./secrets-management.mdx) + +## Troubleshooting + +
+Connection Refused error on startup + +If you see a "Connection Refused" error when starting ToolHive, your container +runtime (Docker, Podman, or Colima) is likely not installed, not running, or not +configured correctly. + +Follow the instructions in the error message to install or start your container +runtime. For example, if you're using Docker Desktop, make sure it's running and +that the Docker daemon is active. + +If the retry button doesn't work, restart ToolHive. + +
+ +
+No system tray icon on Linux + +Recent versions of Fedora Linux and other distributions have removed the +AppIndicator extension from their default installations. ToolHive requires this +extension for the system tray icon to work properly. + +On Fedora, install the `gnome-shell-extension-appindicator` package: + +```bash +sudo dnf install gnome-shell-extension-appindicator +``` + +You'll need to log out and log back in to activate the extension. + +Alternatively, install the +[Extension Manager](https://github.com/mjakeman/extension-manager) app. It's +available as a native package in many distributions, or you can install it from +[Flathub](https://flathub.org/apps/com.mattjakeman.ExtensionManager). Then, use +Extension Manager to install the +[AppIndicator](https://extensions.gnome.org/extension/615/appindicator-support/) +extension (listed as "AppIndicator and KStatusNotifierItem Support"). + +The ToolHive icon should now appear in your system tray. + +
+ +### Other issues + +For other installation issues, check the +[GitHub issues page](https://github.com/stacklok/toolhive-studio/issues) or join +the [Discord community](https://discord.gg/stacklok). diff --git a/versioned_docs/version-1.0/toolhive/guides-ui/mcp-optimizer.mdx b/versioned_docs/version-1.0/toolhive/guides-ui/mcp-optimizer.mdx new file mode 100644 index 00000000..e62e03c1 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-ui/mcp-optimizer.mdx @@ -0,0 +1,120 @@ +--- +title: Optimize MCP tool usage +description: Enable the MCP Optimizer to enhance tool selection and reduce token usage. +--- + +## Overview + +The ToolHive MCP Optimizer acts as an intelligent intermediary between AI +clients and MCP servers. It provides tool discovery, unified access to multiple +MCP servers through a single endpoint, and intelligent routing of requests to +appropriate MCP tools. + +:::info[Status] + +The MCP Optimizer is currently experimental. If you try it out, please share +your feedback on the [Stacklok Discord community](https://discord.gg/stacklok). + +::: + +Learn more about the MCP Optimizer, its benefits, and how it works in the +tutorial: +[Reduce token usage with MCP Optimizer](../tutorials/mcp-optimizer.mdx). + +## Enable MCP Optimizer + +:::note[Limitations] + +MCP Optimizer does not currently work on Linux hosts or with Podman Desktop on +Windows. Supported configurations: + +- macOS with Docker Desktop, Podman Desktop, or Rancher Desktop +- Windows with Docker Desktop or Rancher Desktop + +MCP Optimizer also does not currently work with MCP servers that have network +isolation enabled. + +::: + +To enable the MCP Optimizer in the ToolHive UI: + +1. Open the **Settings** (⚙️) screen and enable **MCP Optimizer** under + **Experimental Features** +2. Click the **Configure** button on the notification that pops up, or go to the + main **MCP Servers** screen and click **MCP Optimizer** in the left sidebar +3. Select the group that contains the MCP servers you want to optimize and click + **Apply Changes** + +ToolHive automatically updates clients that were registered with the selected +group to use the MCP Optimizer. If you want to connect a new client, go to the +group which is enabled for optimization and use the **Manage Clients** button to +register it. + +For best results, connect your client to only the optimized group. If you +connect it to multiple groups, ensure there is no overlap in MCP servers between +the groups to avoid unpredictable behavior. + +:::info[What's happening?] + +When you enable MCP Optimizer, ToolHive automatically creates an internal group +and runs the `mcp-optimizer` MCP server in that group. + +The MCP Optimizer server discovers the MCP servers in the selected group and +builds an index of their tools for intelligent routing. Automatic polling keeps +the index up to date as servers are added or removed from the optimized group. + +ToolHive also disconnects your AI clients from the original MCP server group and +reconnects them to the MCP Optimizer group. + +::: + +## Customize MCP Optimizer settings + +To update the MCP Optimizer's default settings, click the **Advanced** menu and +select **Customize MCP Optimizer configuration**. This opens a form where you +can modify the `mcp-optimizer` MCP server's configuration. + +:::note + +Changes to the MCP Optimizer configuration might cause the feature to stop +working correctly. Only modify these settings if you understand their +implications. + +Generally, you should only need to change the Docker image tag and the +environment variables detailed below. + +::: + +You can customize the MCP Optimizer's behavior using the following environment +variables: + +- `MAX_TOOLS_TO_RETURN`: Number of tools to return from `find_tool` (default: + `8`) +- `TOOL_DISTANCE_THRESHOLD`: Distance threshold for tool similarity (default: + `1.0`) +- `MAX_TOOL_RESPONSE_TOKENS`: Maximum number of tokens to return from + `call_tool` (default: `None`) +- `WORKLOAD_POLLING_INTERVAL`: Polling interval for running MCP servers, in + seconds (default: `60`) +- `REGISTRY_POLLING_INTERVAL`: Polling interval for ToolHive registry, in + seconds (default: `86400`, 24 hours) + +## Disable MCP Optimizer + +To disable the MCP Optimizer, open the **Settings** (⚙️) screen and disable the +**MCP Optimizer** option under the **Experimental Features** section. + +ToolHive stops and removes the MCP Optimizer server and reconnects your clients +to the original MCP server group. + +## Troubleshooting + +If you encounter issues while using the MCP Optimizer, check the logs for +debugging information. On the **MCP Optimizer** page, click the **Advanced** +menu and select **MCP Optimizer logs**. + +## Related information + +- Tutorial: + [Reduce token usage with MCP Optimizer](../tutorials/mcp-optimizer.mdx) +- [Organize MCP servers into groups](./group-management.mdx) diff --git a/versioned_docs/version-1.0/toolhive/guides-ui/network-isolation.mdx b/versioned_docs/version-1.0/toolhive/guides-ui/network-isolation.mdx new file mode 100644 index 00000000..89c53566 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-ui/network-isolation.mdx @@ -0,0 +1,101 @@ +--- +title: Network isolation +description: How to configure network isolation for MCP servers in ToolHive. +--- + +import useBaseUrl from '@docusaurus/useBaseUrl'; +import ThemedImage from '@theme/ThemedImage'; + +Most MCP servers require network access to function properly—for example, to +access APIs, download data, or communicate with other services. However, +malicious or misconfigured servers can also exfiltrate sensitive data or +download unwanted content. + +When you install an MCP server in ToolHive, you can optionally enable _network +isolation_. This feature restricts the MCP server's network access to only the +resources you specify. + +:::note + +Network isolation currently supports HTTP and HTTPS connections only. Other +protocols are not supported. + +::: + +## Enabling network isolation + +Network isolation is available for local MCP servers installed from the registry +or custom servers. It is not available for remote MCP servers, which are hosted +and outside of ToolHive. + +During the MCP server installation, select the **Network isolation** tab in the +configuration form. Click the toggle to enable it. + +When you enable network isolation, any safe default configuration defined in the +registry is pre-loaded in the form. You can accept these defaults or customize +the settings to specify which hosts and ports the MCP server is allowed to +access: + +- **Allowed hosts**:\ + A list of hostnames or IP addresses that the MCP server is allowed to access. + This can include APIs, data sources, or other services that the MCP server + needs to function properly. + + :::tip + + To allow access to all subdomains under a specific domain, add a leading + period (`.`) in front of the hostname. For example, to allow access to all + subdomains of `github.com`, enter `.github.com` in the allowed hosts list. + + ::: + +- **Allowed ports**:\ + A list of ports that the MCP server is allowed to use for outgoing + connections. This can help prevent the MCP server from accessing unauthorized + services or resources. For example, port 443 is the default port for HTTPS + connections. + +:::info[Important] + +If you do not specify any allowed hosts or ports, the MCP server will not be +able to access any external resources, including the internet. This can be +useful for MCP servers that do not require network access or for testing +purposes. + +::: + +## Example configuration + +The configuration pictured below allows the MCP server to access +`api.github.com` and all subdomains of `githubusercontent.com` on port 443 +(HTTPS): + + +
+ +### Accessing other workloads on the same container network + +To allow an MCP server to access other workloads on the same network, you need +to configure network isolation to include the appropriate hostnames and ports. +This is commonly needed when your MCP server needs to communicate with +databases, APIs, or other services that are running on your local host during +development. + +For example, in Docker environments, you can add `host.docker.internal` to +access services on the host. `host.docker.internal` is a special hostname +provided by Docker that resolves to the host machine's IP address from within +containers. + +- **Allowed hosts**: `host.docker.internal` +- **Allowed ports**: `3000` + +## Related information + +- [Run MCP servers](./run-mcp-servers.mdx) diff --git a/versioned_docs/version-1.0/toolhive/guides-ui/playground.mdx b/versioned_docs/version-1.0/toolhive/guides-ui/playground.mdx new file mode 100644 index 00000000..fe681cce --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-ui/playground.mdx @@ -0,0 +1,266 @@ +--- +title: Test MCP servers +description: + Use the playground feature to test and validate MCP servers directly in the + ToolHive UI with AI model providers. +--- + +import useBaseUrl from '@docusaurus/useBaseUrl'; +import ThemedImage from '@theme/ThemedImage'; + +ToolHive's playground lets you test and validate MCP servers directly in the UI +without requiring additional client setup. This streamlined testing environment +helps you quickly evaluate functionality and behavior before deploying MCP +servers to production environments. + +## Key capabilities + +### Instant testing of MCP servers + +Configure your AI model providers, select your MCP servers and tools, and begin +testing immediately in the desktop app. The playground eliminates the friction +of setting up external AI clients just to validate that your MCP servers work +correctly. + +### Detailed interaction logs + +See tool details, parameters, and execution results directly in the UI, ensuring +full visibility into tool performance and responses. Every interaction is +logged, making it easy to understand exactly what your MCP servers are doing and +how they respond to requests. + +### Integrated ToolHive management + +The playground includes a built-in MCP server that lets you manage your other +MCP servers directly through natural language commands. You can list servers, +check their status, start or stop them, and perform other management tasks using +conversational AI. + +## Getting started + +To start using the playground: + +1. **Access the playground**: Click the **Playground** tab in the ToolHive UI + navigation bar. + +2. **Configure provider settings**: Click **Provider Settings** to set up access + to AI model providers: + - **OpenAI**: Enter your OpenAI API key to use GPT models + - **Anthropic**: Add your Anthropic API key for Claude models + - **Google**: Configure Google AI API key for Gemini models + - **xAI**: Set up xAI API key for Grok models + - **Ollama**: Enter the server URL to connect to your local Ollama instance + (default: `http://localhost:11434`) + - **LM Studio**: Enter the server URL from the **Developer** section in LM + Studio where you started the local server (default: + `http://localhost:1234`) + - **OpenRouter**: Add OpenRouter API key for access to multiple model + providers + +3. **Select MCP tools**: Click the tools icon to manage which MCP servers and + tools are available in the playground. + - View all your running MCP servers + - Enable or disable specific tools from each server + - Search and filter tools by name or functionality + - The `toolhive mcp` server is included by default, providing management + capabilities + + + + :::tip + + For more control over tool availability, use + [Customize tools](./customize-tools.mdx) to permanently configure which tools + are enabled for each registry server. The playground tool selection is + temporary and only affects your testing session. + + ::: + +4. **Start testing**: Begin chatting with your chosen AI model. The model will + have access to all enabled MCP tools and can execute them based on your + requests. + +## Using the playground + +### Testing MCP server functionality + +Use the playground to validate that your MCP servers work as expected: + +```text +Can you list all my MCP servers and show their current status? +``` + +The AI will use the `list_servers` tool from the ToolHive MCP server to provide +a comprehensive overview of your server status. + + + +Or test that an individual MCP tool is working as expected: + +```text +Use the GitHub MCP server to search for recent issues in the microsoft/vscode repository +``` + +If you have the GitHub MCP server running, the AI will execute the appropriate +GitHub API calls and return formatted results. + +### Managing servers through conversation + +The ToolHive desktop app automatically starts a dedicated MCP server +(`toolhive mcp`) that orchestrates ToolHive operations through natural language +commands. This approach provides several key benefits: + +- **Unified interface**: Manage your MCP infrastructure using the same + conversational AI interface you use for testing +- **Contextual operations**: The AI understands your current server state and + can make intelligent decisions about which servers to start, stop, or + troubleshoot +- **Reduced complexity**: No need to switch between the chat interface and + traditional UI controls—everything can be done through conversation +- **Audit trail**: All management operations are logged in the same transparent + way as tool executions, providing clear visibility into what actions were + taken + +Take advantage of these integrated ToolHive management tools: + +```text +Start the fetch MCP server for me +``` + +```text +Stop all unhealthy MCP servers +``` + +```text +Show me the logs for the meta-mcp server +``` + +### Validating tool responses + +The playground shows detailed information about each tool execution: + +- **Tool name and description**: What tool was called and its purpose +- **Input parameters**: The exact parameters passed to the tool +- **Execution status**: Whether the tool succeeded or failed +- **Response data**: The complete response from the tool +- **Timing information**: How long the tool took to execute + +This visibility helps you understand exactly how your MCP servers are behaving +and identify any issues with tool implementation or configuration. + +## Recommended practices + +### Provider security + +- Use dedicated API keys for testing that have appropriate rate limits +- Regularly rotate API keys used in development environments +- Consider using API keys with restricted permissions for testing purposes +- When using local providers like Ollama or LM Studio, ensure the server URLs + are only accessible on your local network to prevent unauthorized access + +### Server management + +- Start only the MCP servers you need for testing to improve performance +- Use the playground to validate new server configurations before connecting + them to production AI clients +- Test different combinations of tools to understand how they work together + +### Testing workflow + +1. **Isolated testing**: Test individual MCP servers one at a time to validate + their functionality +2. **Integration testing**: Enable multiple servers to test how they work + together +3. **Performance validation**: Monitor tool execution times and responses under + different loads +4. **Error handling**: Intentionally trigger error conditions to validate proper + error handling + +## Troubleshooting + +### Common issues + +
+Provider not working + +If your provider isn't working: + +1. **For API key-based providers** (OpenAI, Anthropic, Google, xAI, OpenRouter): + - Verify the API key is correct and has proper permissions + - Check that you have sufficient quota or credits with the provider + - Ensure the API key hasn't expired or been revoked + - Try creating a new API key from the provider's dashboard + +2. **For local providers** (Ollama, LM Studio): + - Verify the server is running and accessible at the configured URL + - Check that the server URL is correct and includes the port number + - Ensure no firewall or network settings are blocking the connection + - For LM Studio, confirm you started the server in the **Developer** section + +
+ +
+MCP tools not appearing + +If your MCP server tools aren't showing up: + +1. Verify the MCP server is running (check the **MCP Servers** page) +2. Click the tools icon and ensure the server's tools are enabled +3. Try restarting the MCP server if it shows as unhealthy +4. Check the server logs for any error messages + +
+ +
+Tool execution failing + +If tools are failing to execute: + +1. Check the tool's parameter requirements in the audit log +2. Verify any required secrets or environment variables are configured +3. Ensure the MCP server has necessary permissions (network access, file system + access) +4. Review the server logs for detailed error information + +
+ +## Next steps + +- Learn about [client configuration](./client-configuration.mdx) to connect + ToolHive to external AI applications +- Set up [secrets management](./secrets-management.mdx) for secure handling of + API keys and tokens +- Explore [network isolation](./network-isolation.mdx) for enhanced security + when testing untrusted MCP servers +- Browse the [registry](./registry.mdx) to discover new MCP servers to test in + the playground + +## Related information + +- [Run MCP servers](./run-mcp-servers.mdx) +- [Install ToolHive](./install.mdx) diff --git a/versioned_docs/version-1.0/toolhive/guides-ui/quickstart.mdx b/versioned_docs/version-1.0/toolhive/guides-ui/quickstart.mdx new file mode 100644 index 00000000..89071826 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-ui/quickstart.mdx @@ -0,0 +1,162 @@ +--- +title: 'Quickstart: ToolHive UI' +sidebar_label: Quickstart +description: + A step-by-step guide to installing the ToolHive desktop application and + running your first MCP server. +--- + +In this tutorial, you'll install the ToolHive desktop application and run your +first MCP server. By the end, you'll have a working MCP server that can fetch +content from websites and be used by AI applications like GitHub Copilot or +Cursor. + +## What you'll learn + +- How to install ToolHive on your system +- How to find available MCP servers +- How to run an MCP server +- How to use the server with an AI client application + +## Prerequisites + +Before starting this tutorial, make sure you have: + +- [Docker](https://docs.docker.com/get-docker/) or + [Podman](https://podman-desktop.io/downloads) or + [Colima](https://github.com/abiosoft/colima) installed and running +- A [supported MCP client](../reference/client-compatibility.mdx) like GitHub + Copilot in VS Code, Cursor, Claude Code, and more + +## Step 1: Install the ToolHive UI + +First, download and run the ToolHive installer for your system. + +- **macOS**: download the DMG installer for + [Apple silicon](https://github.com/stacklok/toolhive-studio/releases/latest/download/ToolHive-arm64.dmg) + or + [Intel-based](https://github.com/stacklok/toolhive-studio/releases/latest/download/ToolHive-x64.dmg) + Macs and copy the ToolHive application to your Applications folder +- **Windows**: download and run the + [installer](https://github.com/stacklok/toolhive-studio/releases/latest/download/ToolHive.Setup.exe) + +- **Linux**: download the RPM or DEB package from the + [releases page](https://github.com/stacklok/toolhive-studio/releases/latest) + and install it using your package manager. + +For more detailed installation instructions, see the +[installing ToolHive](../guides-ui/install.mdx) guide. + +## Step 2: Find an MCP server to run + +On the initial splash screen, click **Browse registry**, or open the +**Registry** page from the top menu bar. This page lists the MCP servers in +ToolHive's built-in registry. + +:::info[What is this?] + +ToolHive maintains a curated registry of MCP servers that have been verified to +work correctly. The registry includes information about what each server does +and how to use it. + +::: + +For this tutorial, you'll use the "fetch" server, which is a simple MCP server +that lets AI agents get the contents of a website. Click on the "fetch" server +to start the installation process. + +## Step 3: Install the Fetch MCP server + +The Fetch MCP server doesn't require any special configuration, so you can +install it with the default settings. Click the **Install server** button to +start the installation. + +Once the server is installed, it will appear on the **MCP Servers** page. + +:::info[What's happening?] + +ToolHive downloads the container image for the fetch server, creates a container +with the appropriate security settings, and starts the server. It also sets up a +proxy process that lets your AI agent communicate with the server. + +::: + +## Step 4: Connect an AI client + +On the **Clients** page, you'll see the supported AI clients that ToolHive can +manage for you. When you connect a client, ToolHive configures it to use the MCP +servers you have installed. + +Click the toggle switch to connect the client you want to use. + +:::info[What's happening?] + +When you connect a supported client, ToolHive automatically configures it to use +MCP servers that you install. This means you don't have to manually configure +the client each time you run an MCP server. + +::: + +## Step 5: Use the MCP server with your client + +Now that your MCP server is running and your client is connected to ToolHive, +you can use the MCP server's tools. Open your client and ask the AI to fetch +content from a website. Note that you might need to restart your client for the +changes to take effect. + +For example, try asking: "Can you fetch the content from https://toolhive.dev +and summarize it for me?" + +The AI should be able to use the Fetch MCP server to retrieve the content and +provide a summary. + +:::info[What's happening?] + +When you ask the AI agent to fetch content, the large language model (LLM) +determines that it needs external data. It discovers the fetch tool provided by +your MCP server, instructs the agent to execute the tool with the URL you +specified, then receives and processes the webpage content to create a summary. + +::: + +## What's next? + +Congratulations! You've successfully installed ToolHive and run your first MCP +server. Here are some next steps to explore: + +- Learn more about MCP concepts in the + [MCP primer guide](../concepts/mcp-primer.mdx) +- Try [running more MCP servers](../guides-ui/run-mcp-servers.mdx) from the + registry or from custom sources +- Learn about [secrets management](../guides-ui/secrets-management.mdx) for MCP + servers that require authentication +- Learn how to + [manually configure clients](../reference/client-compatibility.mdx#manual-configuration) + that ToolHive doesn't automatically configure + +## Troubleshooting + +
+Server fails to start + +If the server fails to start, check: + +- Is Docker, Podman, or Colima running? +- Do you have internet access to pull the container image? + +
+ +
+Client can't use the server + +If your AI client application can't use the server: + +- Make sure your client is connected to ToolHive (see Step 4) +- Restart your client to pick up the new configuration +- Verify the server is running on the **MCP Servers** page +- Disconnect and reconnect the client in ToolHive to refresh the configuration + +
+ +If you encounter any problems, please join our +[Discord community](https://discord.gg/stacklok) for help. diff --git a/versioned_docs/version-1.0/toolhive/guides-ui/registry.mdx b/versioned_docs/version-1.0/toolhive/guides-ui/registry.mdx new file mode 100644 index 00000000..f7ac4fcb --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-ui/registry.mdx @@ -0,0 +1,147 @@ +--- +title: Explore the registry +description: How to use the built-in or custom registry to find MCP servers. +--- + +import useBaseUrl from '@docusaurus/useBaseUrl'; +import ThemedImage from '@theme/ThemedImage'; + +ToolHive includes a built-in registry of MCP servers with verified +configurations that meet a +[minimum quality standard](../concepts/registry-criteria.mdx), allowing you to +discover and deploy high-quality tools effortlessly. You can browse the +registry, select servers, and run them securely through the user interface. + +You can also configure ToolHive to use a custom registry instead. This is useful +for organizations that want to maintain their own private registry of MCP +servers. + +## Find MCP servers + +The ToolHive interface includes a dedicated **Registry** section in the main +menu. This section lets you explore the various MCP servers available in either +the built-in or custom registry. + + + +The registry interface displays a list of available servers. You can browse +through the list, use search functionality, and click on any server to view +detailed information before installing it. + +## View server details + +Click on any server in the registry to view detailed information including: + +- **Server name and description**: Full description of what the server does +- **Tier badge**: Shows whether the server is **Official** or **Community** +- **Transport type**: Shows the communication method (e.g., stdio) +- **Popularity**: Star count and download statistics +- **Provenance**: Security verification status (e.g., "Provenance signed by + Sigstore") +- **Tools listed**: Names of all available tools the server provides +- **Action buttons**: **Install server** and **GitHub** (if repository + available) + +This detailed view helps you understand the server's purpose and capabilities +before adding it to your ToolHive installation. + +## Registry groups + +Registry groups allow you to organize related MCP servers and run them together +as a cohesive unit. This is particularly useful for creating team-specific +toolkits, workflow-based server collections, or environment-specific +configurations. + +The default ToolHive registry contains only individual servers. To use groups, +configure a [custom registry](../tutorials/custom-registry.mdx) that defines +them. + +### Key characteristics + +- **Optional**: Groups are entirely optional. You only need a custom registry + with groups if you want to install multiple servers together +- **Independent server definitions**: Each group contains complete server + configurations, not references to top-level servers +- **Self-contained**: Groups can define servers with the same names as + individual servers but with different configurations +- **Flexible membership**: The same server can appear in multiple groups with + different configurations + +### Browse and view groups + +When you use a custom registry that includes groups, they appear in the registry +grid alongside individual servers. Click on any group to view its name, +description, and a list of all included servers with their descriptions. From +the group details page, you can click **Install group** to begin installation. + +### Install a group + +To install a group: + +1. Click **Install group** to open the wizard +2. Configure each server following the same process as + [installing an individual server](./run-mcp-servers.mdx#configure-the-server) +3. Click **Next** after each server, or **Finish** on the last one + +The wizard guides you through each server one at a time, showing your progress +(for example, "Installing server 1 of 3"). After installation, the group appears +in the **MCP Servers** page where you can +[manage the servers together](./group-management.mdx). + +## Registry settings + +To configure your registry, ToolHive provides a dedicated **Registry** settings +section in **Settings** → **Registry** with three options: + +- **Default Registry** - Use ToolHive's built-in verified registry +- **Remote Registry (URL)** - Specify a custom URL for a remote registry hosted + on a web server +- **Local Registry (File Path)** - Specify a local file path to a JSON registry + file on your system + +When you select **Local Registry (File Path)**, an additional **Registry File +Path** field appears where you need to provide the absolute path to your local +JSON registry file (for example: `/path/myregistry/db.json`). Click **Save** to +apply your configuration. + +For detailed information on creating a custom registry, see the +[custom registry tutorial](../tutorials/custom-registry.mdx). + + + +## Next steps + +After exploring the registry and finding servers you want to use: + +- **Install servers**: Click on servers from the registry to install and + configure them for your environment +- **Customize tools**: Selectively enable or disable tools from installed + registry servers to create focused tool sets +- **Manage running servers**: Use the **MCP Servers** section to start, stop, + and monitor your installed servers +- **Create custom registries**: Set up your own private registry for + organization-specific servers + +## Related information + +- [Install ToolHive UI](./install.mdx) - Get started with ToolHive +- [Run MCP servers](./run-mcp-servers.mdx) - Install and manage servers from the + registry +- [Custom registry tutorial](../tutorials/custom-registry.mdx) - Create your own + MCP server registry +- [Registry criteria](../concepts/registry-criteria.mdx) - Quality standards for + built-in registry servers diff --git a/versioned_docs/version-1.0/toolhive/guides-ui/run-mcp-servers.mdx b/versioned_docs/version-1.0/toolhive/guides-ui/run-mcp-servers.mdx new file mode 100644 index 00000000..e2d1afe1 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-ui/run-mcp-servers.mdx @@ -0,0 +1,517 @@ +--- +title: Run MCP servers +description: How to install and run MCP servers in the ToolHive UI. +--- + +import RemoteAuthExamples from '../_partials/_remote-mcp-auth-examples.mdx'; +import Heading from '@theme/Heading'; + +ToolHive makes it easy to run and manage Model Context Protocol (MCP) servers. +This guide walks you through installing MCP servers from the registry, using +Docker images, or from source packages. + +## Install from the registry + +ToolHive includes a built-in registry of MCP servers that meet a +[minimum quality standard](../concepts/registry-criteria.mdx), allowing you to +discover and deploy high-quality tools effortlessly. Simply select one from the +list to run it securely with just a few clicks. + +### Select an MCP server + +{/* prettier-ignore */} +To install an MCP server (local Local MCP icon +or remote {/* prettier-ignore */} Remote MCP icon ) from the registry: + +1. Open the **Registry** page from the menu bar. +1. Browse or search for the MCP server you want to install. +1. Click on the desired MCP server to open its details page. Here you can review + more information about the server like its: + - Description + - Available tools + - Tier (community or official) + - Popularity (GitHub stars) + - A link to the GitHub repository for more details and usage documentation +1. Click the **Install server** button to start the installation process. + +### Configure the server + + + + Local MCP icon + {' '}Local MCP + + } + default> + +**Local MCP servers** run directly on your machine using your container runtime. + +The configuration form is pre-filled with defaults from the registry. Enter the +remaining required information and adjust any optional settings as needed: + +1. **Server name**: A unique name for the MCP server.\ + This defaults to the MCP server's name in the registry. If you want to run + multiple instances of the same MCP server, give each instance a unique name. + +1. **Group**: The group where this server will be added. [Optional]\ + Select an existing group or keep the default. Groups help you organize + servers and control client access. See + [Organize servers into groups](./group-management.mdx) for details. + +1. **Command arguments** (optional):\ + Enter any command-line arguments needed to run the MCP server. These might be + needed to pass application-specific parameters to the MCP server. Refer to + the MCP server's documentation for details. + + :::info + + We've made every effort to include sensible defaults in the registry for a + one-click experience, but some servers may require additional command-line + arguments to function correctly. + + ::: + +1. **Storage volumes** [Optional]:\ + Map files or folders from your local host to the MCP server. See + [Mount host files and folders](#volumes). [Optional] + +1. **Secrets** and **environment variables** expected by the server are + populated from the registry automatically. Required values are marked with an + asterisk (\*). + 1. **Secrets**: Enter a value to create a new secret or select an existing + secret to map to the environment variable. Secrets are stored securely and + can be used by the MCP server without exposing them in plaintext. + 2. **Environment variables**: Enter the value in the input field alongside + the variable name. These are typically used for configuration options that + do not require sensitive data. + +1. **Network isolation** [Optional]:\ + Enable network isolation to restrict the MCP server's network access. This + enhances security by limiting the server's ability to communicate with + external networks. See the [Network isolation](./network-isolation.mdx) guide + for details. + +:::note + +Refer to the MCP server's documentation for the required configuration +information, permissions, and authentication details. A link to the GitHub +repository is provided on each server's details page. + +::: + + + + Remote MCP icon + {' '}Remote MCP + + }> + +**Remote MCP servers** are hosted services that you connect to. + +The configuration form is pre-filled with defaults from the registry. Enter the +remaining required information and adjust any optional settings as needed: + +1. A unique **name** for the MCP server. [Required] +1. **Group**: The group where this server will be added. [Optional]\ + Select an existing group or keep the default. Groups help you organize + servers and control client access. See + [Organize servers into groups](./group-management.mdx) for details. +1. The **URL** of the remote MCP server. [Required] +1. The **transport protocol** that the MCP server uses. [Required]\ + ToolHive supports server-sent events (SSE) and Streamable HTTP (default) for + real-time communication. The protocol must match what the MCP server + supports. +1. **Authorization method**: Choose how ToolHive should authenticate with the + remote server.\ + The default is **Auto-Discovered**. Use this option for MCP servers that + fully implement the MCP authorization spec including dynamic client + registration (RFC7591) or for servers that do not require authentication. + ToolHive automatically: + - Discovers OAuth/OIDC endpoints + - Registers a new OAuth client + - Obtains and manages client credentials + - Handles token lifecycle automatically + + For MCP servers that require manual configuration, ToolHive supports OAuth2 + and OIDC authentication. Obtain the necessary information from the MCP + server's documentation or administrator. + + **OAuth2 authentication options:** + - **Authorize URL**: The URL where users are redirected to authenticate and + authorize access to the MCP server. [Required] + - **Token URL**: The URL where your application exchanges the authorization + code for access tokens. [Required] + - **Client ID**: Your application's identifier registered with the OAuth + provider. [Required] + - **Client secret**: The secret key that proves your application's identity. + Enter a value to create a new secret or select an existing secret from the + provider. Secrets are stored securely and can be used by the MCP server + without exposing them in plaintext. See + [Secrets management](./secrets-management.mdx) for details. [Optional] + - **Scopes**: List of permissions your application is requesting. [Optional] + - **PKCE**: Enable Proof Key for Code Exchange (RFC 7636) for enhanced + security without requiring a client secret. [Optional] + + **OIDC authentication options:** + - **Issuer URL**: The base URL of the OIDC provider. [Required] + - **Client ID**: Your application's identifier registered with the OIDC + provider. [Required] + - **Client secret**: The secret key that proves your application's identity. + Enter a value to create a new secret or select an existing secret from the + provider. Secrets are stored securely and can be used by the MCP server + without exposing them in plaintext. See + [Secrets management](./secrets-management.mdx) for details. [Optional] + - **PKCE**: Enable Proof Key for Code Exchange (RFC 7636) for enhanced + security without requiring a client secret. [Optional] + +1. The **callback port** for the authentication redirect. [Optional] + +1. **Custom headers** [Optional]:\ + Add custom HTTP headers to inject into requests to the remote MCP server. + - **Plaintext headers**: Add static key-value pairs for headers like + `X-Tenant-ID` or correlation IDs. These values are stored and transmitted + in plaintext. + - **Headers from secrets**: For sensitive data like API keys, add headers + whose values are retrieved from ToolHive's secrets manager. Enter a header + name and either select an existing secret or enter a new value to create + one. + +Click **Install server** to connect to the remote MCP server. + +
+View examples of remote MCP authentication configuration + + + +
+ +
+
+ +### Start the MCP server + +Click **Install server** to install and start the MCP server. ToolHive downloads +the Docker image, creates a container, and starts it with the specified +configuration. + +Once the server is running, you can see its status on the **MCP Servers** page. +Each server's card includes: + +- The server name +- Its status (Running or Stopped) with a toggle button to start or stop it +- A menu (︙) that includes the server's URL for AI clients that need manual + configuration, and options to: + - Open the server's GitHub repository + - View the server's logs + - Remove the server + +See [Manage MCP servers](#manage-mcp-servers) below for more details on server +states. + +## Install a custom MCP server + +You're not limited to the MCP servers in the registry. You can run remote MCP +servers by providing a URL, or your own local custom MCP servers using Docker +images or source packages. + +### Configure the server + + + + Local MCP icon + {' '}Custom local MCP + + } + default> + +On the **MCP Servers** page, click **Add an MCP server**, then choose **Add +custom local server** in the drop-down menu. Or if this is your first MCP +server, on the introductory screen. + +In the **Custom MCP server** dialog, choose [Docker image](#from-a-docker-image) +or [Package manager](#from-a-source-package). + +{/* prettier-ignore */} +From a Docker image + +Select the **Docker image** option. This allows you to run any MCP server that +is available as a Docker image in a remote registry or locally on your system. + +On the configuration form, enter: + +1. A unique **name** for the MCP server. [Required] + +1. **Group**: The group where this server will be added. [Optional]\ + Select an existing group or keep the default. Groups help you organize + servers and control client access. See + [Organize servers into groups](./group-management.mdx) for details. + +1. The **transport protocol** that the MCP server uses. [Required]\ + ToolHive supports standard input/output (`stdio`), server-sent events (SSE), + and Streamable HTTP. The protocol must match what the MCP server supports. + +1. The **target port** to expose from the container (SSE or Streamable HTTP + transports only). [Optional]\ + If the MCP server uses a specific port, enter it here. If not specified, + ToolHive will use a random port that is exposed to the container with the + `MCP_PORT` and `FASTMCP_PORT` environment variables. + +1. The Docker **image name** and tag (e.g., `my-mcp-server:latest`). [Required]\ + You can use any valid Docker image, including those hosted on Docker Hub or + other registries. + +1. **Command-line arguments** needed to run the MCP server. [Optional]\ + These are specific to the MCP server and might include transport options or + application-specific parameters. Refer to the MCP server's documentation for + details. + +1. Any **secrets** or **environment variables** required by the MCP server. + [Optional]\ + These might include API tokens, configuration options, or other sensitive + data. + - Secrets are mapped to an environment variable. Enter the variable name and + select an existing secret or enter a value to create a new one. + - For non-sensitive environment variables, enter the name and value directly. + +1. **Storage volumes** [Optional]:\ + Map files or folders from your local host to the MCP server. See + [Mount host files and folders](#volumes). [Optional] + +1. **Network isolation** [Optional]:\ + Enable network isolation to restrict the MCP server's network access. This + enhances security by limiting the server's ability to communicate with + external networks. See the [Network isolation](./network-isolation.mdx) guide + for details. + +Click **Install server** to create and start the MCP server container. + +{/* prettier-ignore */} +From a source package + +Select the **Package manager** option. This allows you to run an MCP server from +a source package. + +ToolHive downloads the MCP server's source package and builds a Docker image +on-the-fly. The following package formats are supported: + +- Node.js-based MCP servers using npm +- Python-based MCP servers available on PyPI, using the `uv` package manager +- Go-based MCP servers available on GitHub + +On the configuration form, enter: + +1. A unique **name** for the MCP server. [Required] + +1. **Group**: The group where this server will be added. [Optional]\ + Select an existing group or keep the default. Groups help you organize + servers and control client access. See + [Organize servers into groups](./group-management.mdx) for details. + +1. The **transport protocol** that the MCP server uses. [Required]\ + ToolHive supports standard input/output (`stdio`), server-sent events (SSE), + and Streamable HTTP. The protocol must match what the MCP server supports. + +1. The **target port** to expose from the container (SSE or Streamable HTTP + transports only). [Optional]\ + If the MCP server uses a specific port, enter it here. If not specified, + ToolHive will use a random port that is exposed to the container with the + `MCP_PORT` and `FASTMCP_PORT` environment variables. + +1. The package **protocol** (`npx`, `uvx`, or `go`). [Required] + +1. The **name** of the package to run. [Required] + 1. For `npx`, use the [npm](https://www.npmjs.com/) package name and version, + e.g. `my-mcp-package@latest` + 2. For `uvx`, use the [PyPI](https://pypi.org/) package name and version, + e.g. `my-mcp-package@latest` + 3. For `go`, use the GitHub repository URL with full path to the `main` + package and version, e.g. + `go://github.com/orgname/my-mcp-server/cmd/server@latest` + +1. **Command-line arguments** needed to run the MCP server. [Optional]\ + These are specific to the MCP server and might include transport options or + application-specific parameters. Refer to the MCP server's documentation for + details. + +1. Any **secrets** or **environment variables** required by the MCP server. + [Optional]\ + These might include API tokens, configuration options, or other sensitive + data. + - Secrets are mapped to an environment variable. Enter the variable name and + select an existing secret or enter a value to create a new one. + - For non-sensitive environment variables, enter the name and value directly. + +1. **Storage volumes** [Optional]:\ + Map files or folders from your local host to the MCP server. See + [Mount host files and folders](#volumes). [Optional] + +1. **Network isolation** [Optional]:\ + Enable network isolation to restrict the MCP server's network access. This + enhances security by limiting the server's ability to communicate with + external networks. See the [Network isolation](./network-isolation.mdx) guide + for details. + +Click **Install server** to create and start the MCP server container. + + + + Remote MCP icon + {' '}Custom remote MCP + + }> + +On the **MCP Servers** page, click **Add an MCP server**, then choose **Add a +remote MCP server** in the drop-down menu. + +On the configuration form, enter: + +1. A unique **name** for the MCP server. [Required] +2. **Group**: The group where this server will be added. [Optional]\ + Select an existing group or keep the default. Groups help you organize + servers and control client access. See + [Organize servers into groups](./group-management.mdx) for details. +3. The **URL** of the remote MCP server. [Required] +4. The **transport protocol** that the MCP server uses. [Required]\ + ToolHive supports server-sent events (SSE) and Streamable HTTP (default) for + real-time communication. The protocol must match what the MCP server + supports. +5. **Authorization method**: Choose how ToolHive should authenticate with the + remote server.\ + The default is **Auto-Discovered**. Use this option for MCP servers that + fully implement the MCP authorization spec including dynamic client + registration (RFC7591) or for servers that do not require authentication. + ToolHive automatically: + - Discovers OAuth/OIDC endpoints + - Registers a new OAuth client + - Obtains and manages client credentials + - Handles token lifecycle automatically + + For MCP servers that require manual configuration, ToolHive supports OAuth2 + and OIDC authentication. Obtain the necessary information from the MCP + server's documentation or administrator. + + **OAuth2 authentication options:** + - **Authorize URL**: The URL where users are redirected to authenticate and + authorize access to the MCP server. [Required] + - **Token URL**: The URL where your application exchanges the authorization + code for access tokens. [Required] + - **Client ID**: Your application's identifier registered with the OAuth + provider. [Required] + - **Client secret**: The secret key that proves your application's identity. + Enter a value to create a new secret or select an existing secret from the + provider. Secrets are stored securely and can be used by the MCP server + without exposing them in plaintext. See + [Secrets management](./secrets-management.mdx) for details. [Optional] + - **Scopes**: List of permissions your application is requesting. [Optional] + + **OIDC authentication options:** + - **Issuer URL**: The base URL of the OIDC provider. [Required] + - **Client ID**: Your application's identifier registered with the OIDC + provider. [Required] + - **Client secret**: The secret key that proves your application's identity. + Enter a value to create a new secret or select an existing secret from the + provider. Secrets are stored securely and can be used by the MCP server + without exposing them in plaintext. See + [Secrets management](./secrets-management.mdx) for details. [Optional] + - **PKCE**: Enable Proof Key for Code Exchange (RFC 7636) for enhanced + security without requiring a client secret. [Optional] + +6. The **callback port** for the authentication redirect. [Optional] + +7. **Custom headers** [Optional]:\ + Add custom HTTP headers to inject into requests to the remote MCP server. + - **Plaintext headers**: Add static key-value pairs for headers like + `X-Tenant-ID` or correlation IDs. These values are stored and transmitted + in plaintext. + - **Headers from secrets**: For sensitive data like API keys, add headers + whose values are retrieved from ToolHive's secrets manager. Enter a header + name and either select an existing secret or enter a new value to create + one. + +Click **Install server** to connect to the remote MCP server. + +
+View examples of remote MCP authentication configuration + + + +
+ +
+
+ +## Mount host files and folders \{#volumes} + +Some MCP servers need access to files on your machine. You can mount host paths +directly in the UI. + +1. In the server **Install / Configure** dialog, scroll to **Storage volumes**. +2. Use the **first row** to create your mount: + - **Host path** — choose a file or folder on your computer. + - **Container path** — where it should appear inside the server (for example, + `/data`). + - By default, mounts are in read-write mode. If you want your volume mount to + be **Read-only**, select the "Read-only access" option from the drop-down. +3. If you need additional mounts, click **Add a volume** and repeat. +4. Click **Install server** to start the server with your volume(s). + +This applies to both registry-installed servers and custom servers (Docker image +or source package). + +## Manage MCP servers + +On the **MCP Servers** page, you can manage your installed MCP servers: + +- **Start/Stop**: Use the toggle button to start or stop the MCP server. When + you stop a server, it remains in the list but is no longer running. ToolHive + removes the server from connected AI clients while stopped. + +Click the menu (︙) on the server card to access these options: + +- **Copy URL** (⧉): Copy the MCP server's URL to your clipboard. This URL is + used by AI clients to connect to the MCP server. +- **Edit configuration**: Modify the server's settings, such as command + arguments, environment variables, secrets, storage volumes, and network + isolation. The dialog is pre-filled with your existing configuration, so you + only need to change the specific settings you want to update. +- **GitHub repository**: View the MCP server's source code and documentation on + GitHub. (Only available for servers installed from the registry using the + default name.) +- **Logs**: View the server's output. For local servers, this shows the + container logs. For remote servers, it shows logs from the proxy process. +- **Remove**: Stop and remove the MCP server from ToolHive. This deletes the + container and any associated configuration, so use with caution. +- **Copy server to a group**: Duplicate the server configuration to a different + group. See [Organize servers into groups](./group-management.mdx) for details. + +When you quit the application, ToolHive prompts you to stop all running MCP +servers. The running servers are recorded and ToolHive restarts them +automatically the next time you launch the application. + +## Next steps + +- Connect ToolHive to AI clients like GitHub Copilot or Cursor using the + [client configuration guide](./client-configuration.mdx). +- Customize which tools are available from registry servers using the + [customize tools guide](./customize-tools.mdx). +- Learn more about [secrets management](./secrets-management.mdx) to securely + manage API tokens and other sensitive data. +- Test your MCP servers using the [playground](./playground.mdx) to validate + functionality and behavior with different models. diff --git a/versioned_docs/version-1.0/toolhive/guides-ui/secrets-management.mdx b/versioned_docs/version-1.0/toolhive/guides-ui/secrets-management.mdx new file mode 100644 index 00000000..2047284e --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-ui/secrets-management.mdx @@ -0,0 +1,62 @@ +--- +title: Secrets management +description: How to securely manage API tokens and other sensitive data in the ToolHive UI. +--- + +Many MCP servers need secrets like API tokens, connection strings, and other +sensitive parameters. ToolHive provides built-in secrets management so you can +manage these values securely without exposing them in plaintext configuration +files. + +ToolHive encrypts secrets using a randomly generated password that is stored in +your operating system's secure keyring. + +You can add new secrets on the **Secrets** page or during MCP server +installation. + +:::note + +The ToolHive UI does not currently support the 1Password secrets provider. If +you have configured the 1Password secrets provider using the ToolHive CLI, the +UI will automatically update your configuration to use the built-in encrypted +provider instead. + +::: + +## Enter secrets during MCP installation + +When you install an MCP server from the registry, any required secrets are +listed on the configuration form with their corresponding environment variable. + +When you add a custom MCP server, add secrets by entering the environment +variable name that the MCP server expects. + +To set the secret value, you can: + +- Select an existing secret to populate its value in the configuration form. +- Enter a value in the input box. ToolHive creates a new secret with a name + matching the environment variable. + +## Manage secrets + +Your ToolHive secrets are managed on the **Secrets** page. Here you can: + +- Click the **Add secret** button to create a new secret. Enter a friendly name + for the secret and its value. +- Expand the menu (︙) next to an existing secret to: + - Update the secret value + - Delete the secret + +:::warning + +If you delete a secret that is in use by an MCP server, the server will continue +running but you will not be able to restart it if stopped. You'll need to remove +the server and reinstall it with the required secrets, or add the secret back +using the same name. + +::: + +## Related information + +- [Run MCP servers](./run-mcp-servers.mdx) +- [Client configuration](./client-configuration.mdx) diff --git a/versioned_docs/version-1.0/toolhive/guides-vmcp/audit-logging.mdx b/versioned_docs/version-1.0/toolhive/guides-vmcp/audit-logging.mdx new file mode 100644 index 00000000..3ff13e1e --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-vmcp/audit-logging.mdx @@ -0,0 +1,410 @@ +--- +title: Audit logging +description: + Configure audit logging for Virtual MCP Server to meet security compliance + requirements and track MCP operations. +--- + +Virtual MCP Server (vMCP) provides comprehensive audit logging for all MCP +operations. Audit logs enable security teams to meet compliance requirements, +investigate incidents, and maintain operational visibility. + +## Overview + +The audit logging system captures structured JSON events for every MCP protocol +operation, including tool calls, resource reads, prompt requests, and composite +workflow executions. The implementation can help organizations address +audit-related compliance requirements for enterprise security. + +## Enable audit logging + +Configure audit logging in the VirtualMCPServer resource using the +`spec.config.audit` field: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp + namespace: toolhive-system +spec: + config: + groupRef: my-group + # highlight-start + audit: + enabled: true + component: vmcp-production + includeRequestData: true + includeResponseData: false + maxDataSize: 4096 + # highlight-end +``` + +### Configuration options + +| Field | Description | Default | +| --------------------- | ------------------------------------------------ | ------------- | +| `enabled` | Enable audit logging | `false` | +| `component` | Component name in audit events | `vmcp-server` | +| `eventTypes` | Specific event types to log (empty = all events) | `[]` (all) | +| `excludeEventTypes` | Event types to exclude from logging | `[]` | +| `includeRequestData` | Include request payloads in audit logs | `false` | +| `includeResponseData` | Include response payloads in audit logs | `false` | +| `maxDataSize` | Maximum payload size in bytes | `1024` | +| `logFile` | File path for audit logs (empty = stdout) | `""` (stdout) | + +## Audit event types + +vMCP logs several categories of events: + +### MCP protocol operations + +Standard MCP protocol interactions are logged with these event types: + +- `mcp_initialize`: MCP connection initialization +- `mcp_tool_call`: Tool invocation +- `mcp_tools_list`: List available tools +- `mcp_resource_read`: Read a resource +- `mcp_resources_list`: List available resources +- `mcp_prompt_get`: Get a prompt +- `mcp_prompts_list`: List available prompts +- `mcp_notification`: MCP notifications +- `mcp_completion`: Completion requests +- `mcp_roots_list_changed`: Root list changed notifications + +### Connection and transport events + +Connection establishment events: + +- `sse_connection`: SSE transport connection established (SSE connections are + logged separately due to their long-lived nature; other transports like + streamable HTTP use standard MCP protocol events) +- `mcp_ping`: Health check pings + +### Logging events + +MCP logging protocol events: + +- `mcp_logging`: Logging messages from MCP servers + +### Composite workflow operations + +Composite tools (multi-step workflows) generate additional audit events: + +- `vmcp_workflow_started`: Workflow execution begins +- `vmcp_workflow_completed`: Workflow completes successfully +- `vmcp_workflow_failed`: Workflow fails +- `vmcp_workflow_timed_out`: Workflow exceeds timeout +- `vmcp_workflow_step_started`: Individual step begins +- `vmcp_workflow_step_completed`: Individual step completes +- `vmcp_workflow_step_failed`: Individual step fails +- `vmcp_workflow_step_skipped`: Conditional step is skipped + +### Fallback event types + +Generic event types for unrecognized requests: + +- `mcp_request`: Generic MCP request when specific type cannot be determined +- `http_request`: Generic HTTP request (non-MCP) + +## Filter audit events + +By default, all event types are logged. You can filter events using `eventTypes` +(allowlist) and `excludeEventTypes` (blocklist): + +```yaml +spec: + config: + audit: + enabled: true + eventTypes: + - mcp_initialize + - mcp_tool_call + - vmcp_workflow_started + - vmcp_workflow_completed + - vmcp_workflow_failed + excludeEventTypes: + - mcp_ping +``` + +Use `eventTypes` to capture only specific operations. Use `excludeEventTypes` to +filter out high-frequency events like health checks. + +:::info + +When both fields are specified, `excludeEventTypes` takes precedence. Events +matching the exclusion list are never logged, even if they appear in +`eventTypes`. + +::: + +## Payload logging + +By default, audit logs capture metadata about operations but not the actual +request and response payloads. For forensic analysis or debugging, you can +enable payload capture: + +```yaml +spec: + config: + audit: + enabled: true + includeRequestData: true + includeResponseData: true + maxDataSize: 16384 # Truncate payloads larger than 16KB +``` + +The `maxDataSize` field controls the maximum size of captured payloads to +prevent log bloat. Payloads exceeding this limit are truncated. + +:::warning + +Request and response payloads may contain sensitive data. Review your +organization's data handling policies before enabling payload logging in +production environments. + +::: + +## Audit log format + +Each audit event is a structured JSON object with these fields: + +```json +{ + "time": "2025-02-02T15:45:30.123456789Z", + "level": "INFO+2", + "msg": "audit_event", + "audit_id": "a3f2b8d1-4c5e-6789-abcd-ef0123456789", + "type": "mcp_tool_call", + "logged_at": "2025-02-02T15:45:30.123456Z", + "outcome": "success", + "component": "vmcp-production", + "source": { + "type": "network", + "value": "10.0.1.50", + "extra": { + "user_agent": "Claude/1.0" + } + }, + "subjects": { + "user": "alice@company.com", + "user_id": "sub-alice-123", + "client_name": "Claude Desktop", + "client_version": "1.0.0" + }, + "target": { + "endpoint": "/mcp", + "method": "tools/call", + "type": "tool", + "name": "github_create_pr" + }, + "metadata": { + "extra": { + "duration_ms": 234, + "transport": "http", + "backend_name": "github" + } + }, + "data": { + "request": { + "title": "Add new feature", + "base": "main" + } + } +} +``` + +### Field descriptions + +| Field | Description | +| ----------- | --------------------------------------------------------- | +| `time` | Timestamp when the log was generated | +| `level` | Log level (INFO+2 for audit events) | +| `msg` | Always "audit_event" for audit logs | +| `audit_id` | Unique identifier for this audit event | +| `type` | Event classification (what happened) | +| `logged_at` | UTC timestamp when the event occurred | +| `outcome` | Result (success, failure, denied, error) | +| `component` | System component that generated the event | +| `source` | Request origin (IP address, user agent) | +| `subjects` | Identity information (user, client) | +| `target` | Resource or operation targeted | +| `metadata` | Additional context (duration_ms, transport, backend_name) | +| `data` | Optional request and response payloads | + +## User identity in audit logs + +The audit system extracts user identity from OIDC authentication tokens into the +subjects field (shown in the audit log format above). + +The user field is populated using this fallback order from token claims: + +1. `name` claim (full name) +2. `preferred_username` claim (username) +3. `email` claim (email address) +4. `"anonymous"` (when authentication is disabled) + +The `user_id` field contains the OIDC `sub` claim (subject identifier). + +:::note + +When using anonymous authentication (not recommended for production), audit logs +show `"user": "anonymous"` with no `user_id`. This may not meet compliance +requirements for user identification. + +::: + +## Configure output destination + +### Log to stdout (default) + +By default, audit logs are written to standard output, which integrates with +Kubernetes log collection: + +```yaml +spec: + config: + audit: + enabled: true + # logFile not specified = stdout +``` + +This approach works well with log aggregation systems like Fluentd, Fluent Bit, +or cloud provider log collectors (CloudWatch, Google Cloud Logging, Azure +Monitor). + +### Log to a file + +To persist audit logs to a file, configure a persistent volume: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp + namespace: toolhive-system +spec: + config: + audit: + enabled: true + logFile: /var/log/audit/vmcp.log + podTemplateSpec: + spec: + volumes: + - name: audit-logs + persistentVolumeClaim: + claimName: vmcp-audit-pvc + containers: + - name: vmcp + volumeMounts: + - name: audit-logs + mountPath: /var/log/audit +``` + +:::info + +File-based audit logs are written with secure permissions (`0600`) that allow +read/write access only to the file owner (the user the vMCP container runs as). + +To access these logs from persistent volumes or log collection sidecars, ensure: + +- The vMCP container user is explicitly configured via + `podTemplateSpec.spec.securityContext.runAsUser` +- Log collection sidecars run as the same user, or +- Use `podTemplateSpec.spec.securityContext.fsGroup` to enable group-based + access + +For most deployments, using the default stdout logging is simpler and integrates +better with Kubernetes log collection systems. + +::: + +## Configuration patterns + +### Security compliance + +For environments requiring comprehensive audit trails: + +```yaml +spec: + config: + audit: + enabled: true + component: vmcp-production + # No eventTypes filter = log all events (comprehensive) + excludeEventTypes: + - mcp_ping # Exclude health checks (not audit-relevant) + includeRequestData: true + includeResponseData: true + maxDataSize: 16384 + logFile: /var/log/vmcp/audit.log +``` + +### Performance-optimized + +For high-throughput environments, log only critical events: + +```yaml +spec: + config: + audit: + enabled: true + component: vmcp-high-throughput + eventTypes: + - mcp_tool_call + - vmcp_workflow_failed + includeRequestData: false + includeResponseData: false +``` + +## Query and analyze audit logs + +### Search for specific operations + +Query logs for a specific user's tool calls: + +```bash +kubectl logs -n toolhive-system -l app.kubernetes.io/instance=my-vmcp \ + | jq 'select(.type == "mcp_tool_call" and .subjects.user == "alice@company.com")' +``` + +### Analyze workflow failures + +Find all failed workflows in the last hour: + +```bash +kubectl logs -n toolhive-system -l app.kubernetes.io/instance=my-vmcp --since=1h \ + | jq 'select(.type == "vmcp_workflow_failed")' +``` + +### Track backend usage + +Count tool calls per backend MCP server: + +```bash +kubectl logs -n toolhive-system -l app.kubernetes.io/instance=my-vmcp \ + | jq -r 'select(.type == "mcp_tool_call") | .metadata.extra.backend_name' \ + | sort | uniq -c +``` + +## Integrate with log collection systems + +When audit logs are written to stdout (the default), they integrate with +standard Kubernetes log collection infrastructure. Your existing log collectors +(Fluentd, Fluent Bit, Filebeat, Splunk forwarders) can parse the JSON audit +events and route them to your observability backend. + +For detailed configuration examples and best practices for setting up log +collection with Fluentd, Filebeat, Splunk, and other systems, see the +[Kubernetes logging guide](../guides-k8s/logging.mdx#set-up-log-collection). + +## Related information + +- [Authentication](./authentication.mdx) - Configure client and backend + authentication for user identity in audit logs +- [Telemetry and metrics](./telemetry-and-metrics.mdx) - Monitor vMCP + performance with OpenTelemetry +- [Observability concepts](../concepts/observability.mdx) - Overview of + ToolHive's observability architecture +- [Kubernetes logging guide](../guides-k8s/logging.mdx) - Configure logging for + MCP servers in Kubernetes diff --git a/versioned_docs/version-1.0/toolhive/guides-vmcp/authentication.mdx b/versioned_docs/version-1.0/toolhive/guides-vmcp/authentication.mdx new file mode 100644 index 00000000..4a2c9036 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-vmcp/authentication.mdx @@ -0,0 +1,164 @@ +--- +title: Authentication +description: Configure client and backend authentication for vMCP. +--- + +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 + +```mermaid +flowchart LR + subgraph Boundary1[" "] + direction TB + Client[MCP Client] + B1Label["**Boundary 1**
Client → vMCP"] + end + + subgraph vMCP["Virtual MCP Server (vMCP)"] + Auth[Token validation] + Backend[Backend auth] + end + + subgraph Boundary2[" "] + direction TB + B2Label["**Boundary 2**
vMCP → Backend APIs"] + GitHub[GitHub API] + Jira[Jira API] + end + + Client -->|"vMCP-scoped
token"| Auth + Auth --> Backend + Backend -->|"Backend-scoped
token"| GitHub + Backend -->|"Backend-scoped
token"| Jira +``` + +**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):** 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 vMCP. + +### Anonymous (development only) + +No authentication required: + +```yaml title="VirtualMCPServer resource" +spec: + incomingAuth: + type: anonymous +``` + +:::warning + +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 title="VirtualMCPServer resource" +spec: + incomingAuth: + type: oidc + oidcConfig: + type: inline + inline: + issuer: https://auth.example.com + 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 title="VirtualMCPServer resource" +spec: + incomingAuth: + type: oidc + oidcConfig: + type: inline + inline: + issuer: https://auth.example.com + clientId: + audience: vmcp + clientSecretRef: + name: oidc-client-secret + key: clientSecret +``` + +Create the Secret: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: oidc-client-secret + namespace: toolhive-system +type: Opaque +stringData: + clientSecret: +``` + +### Kubernetes service account tokens + +Authenticate using Kubernetes service account tokens for in-cluster clients: + +```yaml title="VirtualMCPServer resource" +spec: + incomingAuth: + type: oidc + oidcConfig: + type: kubernetes + kubernetes: + audience: toolhive +``` + +This configuration uses the Kubernetes API server as the OIDC issuer and +validates service account tokens. The defaults work for most clusters: + +- **issuer**: `https://kubernetes.default.svc` (auto-detected) +- **audience**: `toolhive` (configurable) + +## Outgoing authentication + +Configure how vMCP authenticates to backend MCP servers. + +### Discovery mode + +When using discovery mode, vMCP checks each backend MCPServer's +`externalAuthConfigRef` to determine how to authenticate. If a backend has no +auth config, vMCP connects without authentication. + +```yaml title="VirtualMCPServer resource" +spec: + outgoingAuth: + source: discovered +``` + +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) +- [VirtualMCPServer configuration](./configuration.mdx) +- [Token exchange in Kubernetes](../guides-k8s/token-exchange-k8s.mdx) diff --git a/versioned_docs/version-1.0/toolhive/guides-vmcp/backend-discovery.mdx b/versioned_docs/version-1.0/toolhive/guides-vmcp/backend-discovery.mdx new file mode 100644 index 00000000..9a9fb586 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-vmcp/backend-discovery.mdx @@ -0,0 +1,734 @@ +--- +title: Backend discovery modes +description: Choose between discovered and inline backend discovery for Virtual MCP Server. +--- + +Virtual MCP Server (vMCP) supports two backend discovery modes, allowing you to +optimize for either operational convenience (discovered mode with declarative +backend management) or security (inline mode with minimal permissions). + +## Overview + +The deployment mode is configured via the `spec.outgoingAuth.source` field in +the VirtualMCPServer resource. + +| Mode | Backend Discovery | RBAC Requirements | K8s API Access | +| ------------ | ------------------------- | -------------------------------------- | ------------------ | +| `discovered` | Runtime from K8s API | Namespace-scoped read + status updates | Yes | +| `inline` | Inline from configuration | Minimal (status updates only) | No (except status) | + +:::note[Design rationale] + +Backend discovery is configured via `outgoingAuth.source` because the +authentication strategy and backend source are tightly coupled: + +- **Discovered backends** (`source: discovered`) are found at runtime by + querying the Kubernetes API and can reference MCPExternalAuthConfig resources + for their authentication +- **Inline backends** (`source: inline`) are defined in the configuration and + require their authentication to be explicitly configured in + `outgoingAuth.backends` + +This coupling ensures authentication configuration matches the backend discovery +method, preventing misconfigurations. + +::: + +### When to use discovered mode + +Choose discovered mode when: + +- Backends change frequently and you want declarative management via Kubernetes + resources +- Centralized authentication via MCPExternalAuthConfig is preferred +- Namespace-scoped read permissions are acceptable + +### When to use inline mode + +Choose inline mode when: + +- Security or compliance requires minimal permissions and attack surface +- Backend configuration is stable and changes infrequently +- Explicit control over all backend details is required (zero-trust, air-gapped + environments) + +### Trade-offs comparison + +| Consideration | Discovered Mode | Inline Mode | +| ------------------------------ | ------------------------------------------------ | ----------------------------------------- | +| Backend management | Declarative (K8s resources) | Explicit (in configuration) | +| Configuration changes | Add/remove resources without vMCP config changes | Update vMCP config and restart | +| RBAC permissions | Namespace read access | Minimal (status updates only) | +| Attack surface | Larger (K8s API access) | Smaller (no backend discovery API access) | +| Auth management | Centralized (MCPExternalAuthConfig) | Duplicated in YAML | +| Individual backend pod updates | Supported without vMCP changes | Requires vMCP awareness | + +## Discovered mode + +Discovered mode queries the Kubernetes API at runtime to find backend MCP +servers. This is the default mode. + +### Discovered mode configuration + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp + namespace: toolhive-system +spec: + config: + groupRef: my-group + incomingAuth: + type: anonymous + outgoingAuth: + source: discovered # Discover backends at runtime (default) +``` + +### How it works + +When vMCP starts in discovered mode: + +1. **Group verification**: Verifies the referenced MCPGroup exists +2. **Workload discovery**: Queries all MCPServer and MCPRemoteProxy resources in + the group +3. **Backend conversion**: For each workload: + - Extracts service URL and transport type + - Resolves authentication from `externalAuthConfigRef` if configured + - Adds metadata labels +4. **Capability querying**: Calls each backend's `initialize` method to discover + available tools, resources, and prompts +5. **Status updates**: Reports backend health in the VirtualMCPServer status + +### Discovered mode RBAC + +The operator automatically creates the required RBAC resources (ServiceAccount, +Role, and RoleBinding) when you create a VirtualMCPServer resource. + +For reference, the vMCP service account needs read access to: + +- `configmaps`, `secrets`: Read OIDC configs and auth secrets +- `mcpgroups`: Verify group exists and list members +- `mcpservers`, `mcpremoteproxies`: Discover backend workloads +- `mcpexternalauthconfigs`: Resolve authentication configurations +- `mcptoolconfigs`: Resolve tool filtering and renaming +- `virtualmcpservers/status`: Update status with discovered backends + +### Runtime updates + +When backend resources are added, modified, or removed in the group: + +1. The change does NOT automatically trigger vMCP to rediscover backends +2. vMCP continues using the backend list from startup +3. To pick up changes, restart the vMCP pod: + +```bash +kubectl rollout restart deployment vmcp-my-vmcp -n toolhive-system +``` + +## Inline mode + +Inline mode uses pre-configured backends defined in the VirtualMCPServer +resource. This eliminates the need for Kubernetes API access (except status +updates), reducing the attack surface. + +### Inline mode configuration + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp + namespace: toolhive-system +spec: + config: + groupRef: my-group + # highlight-start + backends: + - name: github-mcp + url: http://github-mcp.toolhive-system.svc.cluster.local:8080 + transport: sse + - name: fetch-mcp + url: http://fetch-mcp.toolhive-system.svc.cluster.local:8080 + transport: streamable-http + # highlight-end + incomingAuth: + type: anonymous + outgoingAuth: + # highlight-start + source: inline # Use inline backend configuration + # highlight-end + backends: + github-mcp: + type: external_auth_config_ref + externalAuthConfigRef: + name: github-token-config +``` + +:::note + +The `groupRef` field is required in both discovered and inline modes. In inline +mode, the group reference is used for organizational purposes and status +reporting, even though backends are defined inline rather than discovered from +the group. + +::: + +:::info + +Backend authentication in `outgoingAuth.backends` uses references to +[MCPExternalAuthConfig](../reference/crd-spec.md#apiv1alpha1mcpexternalauthconfig) +resources, not inline configuration. Create the MCPExternalAuthConfig resource +first, then reference it by name. See the +[Authentication guide](./authentication.mdx) for complete examples. + +::: + +### Backend configuration + +Each backend in `spec.config.backends` requires: + +| Field | Description | Required | +| ----------- | -------------------------------------------------------- | -------- | +| `name` | Backend identifier (must match auth config keys) | Yes | +| `url` | Backend MCP server URL (must be `http://` or `https://`) | Yes | +| `transport` | MCP transport protocol (`sse` or `streamable-http`) | Yes | +| `metadata` | Custom labels for the backend | No | + +### Inline mode RBAC + +Inline mode requires minimal permissions: + +- `virtualmcpservers/status`: Update status (only permission needed) + +The operator still creates RBAC resources for status updates, but the vMCP pod +does not query the Kubernetes API for backend discovery. + +## Verify backend status + +### Check VirtualMCPServer status + +View discovered backends and their health: + +```bash +kubectl get virtualmcpserver my-vmcp -n toolhive-system -o yaml +``` + +The status includes: + +```yaml +status: + phase: Ready # Pending|Ready|Degraded|Failed + backendCount: 2 + discoveredBackends: + - name: github-mcp + url: http://github-mcp.toolhive-system.svc.cluster.local:8080 + status: ready + authConfigRef: github-token-config + authType: token_exchange + lastHealthCheck: '2025-02-02T15:30:00Z' + message: Healthy + circuitBreakerState: closed + circuitLastChanged: '2025-02-02T10:00:00Z' + consecutiveFailures: 0 + - name: fetch-mcp + url: http://fetch-mcp.toolhive-system.svc.cluster.local:8080 + status: ready + authType: unauthenticated + lastHealthCheck: '2025-02-02T15:30:00Z' + message: Healthy + consecutiveFailures: 0 +``` + +### Query the status endpoint + +vMCP exposes an unauthenticated `/status` HTTP endpoint for operational +monitoring: + +```bash +kubectl port-forward -n toolhive-system svc/vmcp-my-vmcp 4483:4483 +curl http://localhost:4483/status +``` + +Response format: + +```json +{ + "backends": [ + { + "name": "github-mcp", + "health": "healthy", + "transport": "sse", + "auth_type": "token_exchange" + }, + { + "name": "fetch-mcp", + "health": "healthy", + "transport": "streamable-http", + "auth_type": "unauthenticated" + } + ], + "healthy": true, + "version": "v1.2.3", + "group_ref": "my-group" +} +``` + +**Health states:** + +| State | Description | CRD Status | +| ----------------- | ------------------------------------------------------------------------------------------------------------ | ------------- | +| `healthy` | Backend responds to health checks successfully and quickly (under 5s) | `ready` | +| `degraded` | Health checks succeed but response times exceed 5 seconds (slow), or backend recently recovered from failure | `degraded` | +| `unhealthy` | Backend not responding or health checks timing out (timeout controlled by `healthCheckTimeout`, default 10s) | `unavailable` | +| `unauthenticated` | Authentication to backend failed (internal tracking only) | `unavailable` | +| `unknown` | Health check not yet performed (initial state) | `unknown` | + +:::note[Status terminology] + +The `/status` HTTP endpoint uses internal health values (`healthy`, `degraded`, +`unhealthy`, `unauthenticated`, `unknown`) for debugging. + +The VirtualMCPServer CRD uses user-facing status values (`ready`, `degraded`, +`unavailable`, `unknown`) as shown in the "CRD Status" column above. + +Note: `unauthenticated` is tracked separately for diagnostics but represents an +authentication failure reason, not a distinct health state—it maps to +`unavailable` in the CRD. + +::: + +:::info[Unauthenticated access] + +The `/status` endpoint is unauthenticated for operator consumption. It exposes +operational metadata but does not include secrets, tokens, internal URLs, or +request data. + +::: + +:::tip + +To configure health check intervals, timeouts, thresholds, and circuit breaker +settings, see the +[Operational configuration](./configuration.mdx#operational-configuration) +section. + +::: + +## Switch deployment modes + +Switching between modes requires updating the VirtualMCPServer resource and +restarting the vMCP pod. + +### From discovered to inline + +1. List current backends to capture their configuration: + + ```bash + kubectl get virtualmcpserver my-vmcp -n toolhive-system \ + -o json | jq '.status.discoveredBackends' + ``` + +2. Update the VirtualMCPServer to inline mode: + + ```yaml + spec: + config: + groupRef: my-group + backends: + - name: github-mcp + url: http://github-mcp.toolhive-system.svc.cluster.local:8080 + transport: sse + # Add all backends from status.discoveredBackends + outgoingAuth: + source: inline + ``` + +3. The operator automatically restarts the vMCP pod with the new configuration + +4. Optionally reduce RBAC permissions by removing read access to MCPServer and + MCPRemoteProxy resources (keep status update permissions) + +### From inline to discovered + +1. Ensure backend MCPServer and MCPRemoteProxy resources exist in the group + +2. Update the VirtualMCPServer to discovered mode: + + ```yaml + spec: + config: + groupRef: my-group + # Remove backends array + outgoingAuth: + source: discovered + ``` + +3. Verify RBAC permissions are configured (operator creates them automatically) + +4. The operator automatically restarts the vMCP pod with the new configuration + +5. Check status to verify backends were discovered: + + ```bash + kubectl get virtualmcpserver my-vmcp -n toolhive-system \ + -o json | jq '.status.discoveredBackends' + ``` + +## Complete example + +Here's a complete example showing all required resources for discovered mode +with authentication: + +```yaml +--- +# 1. Create the MCPGroup +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPGroup +metadata: + name: engineering-tools + namespace: toolhive-system +spec: + description: Engineering team MCP servers + +--- +# 2. Create authentication config for GitHub backend +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPExternalAuthConfig +metadata: + name: github-token-config + namespace: toolhive-system +spec: + type: tokenExchange + tokenExchange: + tokenUrl: https://oauth.example.com/token + clientId: github-mcp-client + clientSecretRef: + name: github-oauth-secret + key: client-secret + audience: github-api + +--- +# 3. Create backend MCPServer +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: github-mcp + namespace: toolhive-system +spec: + groupRef: engineering-tools + image: ghcr.io/example/github-mcp-server:v1.2.3 + transport: sse + externalAuthConfigRef: + name: github-token-config + +--- +# 4. Create VirtualMCPServer (discovered mode) +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: engineering-vmcp + namespace: toolhive-system +spec: + config: + groupRef: engineering-tools + incomingAuth: + type: oidc + oidc: + issuer: https://auth.company.com + audience: engineering-vmcp + outgoingAuth: + source: discovered # Discovers github-mcp and its auth config +``` + +Apply all resources: + +```bash +kubectl apply -f vmcp-complete-example.yaml +``` + +Verify backends were discovered: + +```bash +kubectl get virtualmcpserver engineering-vmcp -n toolhive-system \ + -o json | jq '.status.discoveredBackends' +``` + +## Troubleshooting + +
+Backends not appearing in status + +**Symptoms:** + +- `status.discoveredBackends` is empty or missing backends +- `status.backendCount` is 0 or lower than expected + +**Possible causes and solutions:** + +1. **MCPGroup not in Ready state** + + ```bash + kubectl get mcpgroup my-group -n toolhive-system + ``` + + Wait for the group to reach Ready state before starting vMCP. + +2. **Backend resources not referencing the correct group** + + ```bash + kubectl get mcpserver,mcpremoteproxy -n toolhive-system \ + -o custom-columns=NAME:.metadata.name,GROUP:.spec.groupRef + ``` + + Ensure all backends have `spec.groupRef` matching the VirtualMCPServer's + `spec.config.groupRef`. + +3. **vMCP pod not restarted after backend changes** + + Backend changes require a pod restart to be discovered: + + ```bash + kubectl rollout restart deployment vmcp-my-vmcp -n toolhive-system + ``` + +4. **RBAC permissions missing (discovered mode)** + + Check the vMCP service account has required permissions: + + ```bash + kubectl get role -n toolhive-system | grep vmcp + kubectl describe role vmcp-my-vmcp -n toolhive-system + ``` + + The operator should create these automatically. If missing, delete and + recreate the VirtualMCPServer resource. + +
+ +
+Backends showing as unavailable + +**Symptoms:** + +- `status.discoveredBackends[].status` is `unavailable` or `unknown` +- `/status` endpoint shows `health: unhealthy` + +**Possible causes and solutions:** + +1. **Backend pod not running** + + ```bash + kubectl get pods -n toolhive-system -l app.kubernetes.io/name=my-backend + ``` + + Check backend pod logs for errors: + + ```bash + kubectl logs -n toolhive-system deployment/my-backend + ``` + +2. **Backend service not accessible** + + Test connectivity from vMCP pod: + + ```bash + kubectl exec -n toolhive-system deployment/vmcp-my-vmcp -- \ + wget -O- http://my-backend:8080/health + ``` + +3. **Authentication failing** + + Check vMCP logs for auth errors: + + ```bash + kubectl logs -n toolhive-system deployment/vmcp-my-vmcp | grep ERROR + ``` + + Common auth issues: + - Invalid OIDC configuration in MCPExternalAuthConfig + - Expired or invalid client secrets + - Token exchange endpoint unreachable + +4. **Backend returning errors on initialize** + + The backend may be misconfigured or failing to start properly. Check backend + logs and ensure it responds correctly to MCP `initialize` requests. + +
+ +
+RBAC permission errors + +**Symptoms:** + +- vMCP logs show `forbidden` or `unauthorized` errors +- Backends not being discovered in discovered mode + +**Error examples:** + +```text +Failed to list MCPServers: mcpservers.toolhive.stacklok.dev is forbidden: +User "system:serviceaccount:toolhive-system:vmcp-my-vmcp" cannot list +resource "mcpservers" +``` + +**Solutions:** + +1. **Verify service account and role binding exist** + + ```bash + kubectl get serviceaccount vmcp-my-vmcp -n toolhive-system + kubectl get role vmcp-my-vmcp -n toolhive-system + kubectl get rolebinding vmcp-my-vmcp -n toolhive-system + ``` + +2. **Check role permissions** + + ```bash + kubectl describe role vmcp-my-vmcp -n toolhive-system + ``` + + Required permissions for discovered mode: + - `configmaps`, `secrets`: get, list, watch + - `mcpgroups`, `mcpservers`, `mcpremoteproxies`: get, list, watch + - `mcpexternalauthconfigs`, `mcptoolconfigs`: get, list, watch + - `virtualmcpservers/status`: update, patch + +3. **Recreate RBAC resources** + + If RBAC resources are missing or incorrect, delete and recreate the + VirtualMCPServer: + + ```bash + kubectl delete virtualmcpserver my-vmcp -n toolhive-system + kubectl apply -f my-vmcp.yaml + ``` + + The operator will recreate all RBAC resources automatically. + +
+ +
+Mode switching issues + +**Symptoms:** + +- vMCP pod fails to start after switching modes +- Configuration validation errors + +**Switching from discovered to inline:** + +Ensure you define `spec.config.backends[]` before changing `source` to `inline`: + +```yaml +spec: + config: + backends: [] # ❌ Empty array will fail validation +``` + +**Switching from inline to discovered:** + +Remove the `spec.config.backends[]` array when switching to discovered mode: + +```yaml +spec: + config: + backends: [...] # ❌ Should be removed in discovered mode +``` + +
+ +
+Health check failures + +**Symptoms:** + +- `/status` endpoint shows backends as `degraded` or `unhealthy` +- Intermittent backend availability + +**Possible causes:** + +1. **Backend service overloaded or slow** + + Health checks timeout after 10 seconds (default `healthCheckTimeout`). If + backends are slow to respond, they'll be marked unhealthy even if functional. + +2. **Network issues between vMCP and backends** + + Check network policies and service mesh configuration that might block or + slow connections. + +3. **Backend requires authentication for initialize** + + Ensure `externalAuthConfigRef` is properly configured if the backend requires + authentication. + +
+ +
+Configuration validation errors + +**Missing groupRef:** + +```text +Error: spec.config.groupRef is required +``` + +**Fix:** Add `spec.config.groupRef` referencing an existing MCPGroup. + +**Invalid backend URL in inline mode:** + +```text +Error: spec.config.backends[0].url must start with http:// or https:// +``` + +**Fix:** Ensure backend URLs use proper scheme: + +```yaml +backends: + - name: my-backend + url: http://my-backend.default.svc.cluster.local:8080 # Valid + # url: my-backend:8080 # Invalid +``` + +**Missing backends array in inline mode:** + +```text +Error: spec.config.backends is required when outgoingAuth.source is "inline" +``` + +**Fix:** Define at least one backend in `spec.config.backends` when using inline +mode. + +**Invalid transport protocol:** + +```text +Error: spec.config.backends[0].transport must be "sse" or "streamable-http" +``` + +**Fix:** Use only supported transport protocols: + +```yaml +backends: + - name: my-backend + transport: sse # Valid + # transport: stdio # Invalid for inline backends +``` + +**Referenced MCPExternalAuthConfig not found:** + +```text +Error: MCPExternalAuthConfig "github-token-config" not found in namespace "toolhive-system" +``` + +**Fix:** Create the MCPExternalAuthConfig resource before referencing it, or +remove the auth reference. + +
+ +## Related information + +- [Configure vMCP servers](./configuration.mdx) +- [Authentication](./authentication.mdx) +- [VirtualMCPServer CRD specification](../reference/crd-spec.md#apiv1alpha1virtualmcpserver) diff --git a/versioned_docs/version-1.0/toolhive/guides-vmcp/composite-tools.mdx b/versioned_docs/version-1.0/toolhive/guides-vmcp/composite-tools.mdx new file mode 100644 index 00000000..6656d962 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-vmcp/composite-tools.mdx @@ -0,0 +1,637 @@ +--- +title: Composite tools and workflows +description: Create multi-step workflows that span multiple backend MCP servers. +--- + +Composite tools let you define multi-step workflows that execute across multiple +backend MCP servers with parallel execution, conditional logic, approval gates, +and error handling. + +## Overview + +A composite tool combines multiple backend tool calls into a single workflow. +When a client calls a composite tool, vMCP orchestrates the execution across +backend MCP servers, handling dependencies and collecting results. + +## Key capabilities + +- **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 +- **Timeouts**: Workflow and per-step timeout configuration + +:::info + +Elicitation (user prompts during workflow execution) is defined in the CRD but +has not been extensively tested. Test thoroughly in non-production environments +first. + +::: + +## Configuration location + +Composite tools are defined in the VirtualMCPServer resource under +`spec.config.compositeTools`: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp +spec: + incomingAuth: + type: anonymous + config: + groupRef: my-tools + # ... other configuration ... + compositeTools: + - name: my_workflow + description: A multi-step workflow + parameters: + # Input parameters (JSON Schema) + steps: + # Workflow steps +``` + +For complex, reusable workflows, you can also reference external +`VirtualMCPCompositeToolDefinition` resources using +`spec.config.compositeToolRefs`. + +## Simple example + +Here's a composite tool that searches arXiv for papers on a topic and reads the +top result. This example assumes you have an MCPServer resource named `arxiv` in +a group that your vMCP server references, and that you're using the default +[conflict resolution strategy](./tool-aggregation.mdx#conflict-resolution-strategies) +and prefix format (`_`): + +```yaml title="VirtualMCPServer resource" +spec: + config: + compositeTools: + - name: research_topic + description: Search arXiv for papers and read the top result + parameters: + type: object + properties: + query: + type: string + description: Research topic to search for + required: + - query + steps: + # Step 1: Search arXiv for papers matching the query + - id: search + tool: arxiv_search_papers + arguments: + query: '{{.params.query}}' + max_results: 1 + # Step 2: Download the paper (required before reading) + # Note: fromJson is needed when the MCP server returns JSON as text + # rather than structured content. This is common for servers that + # don't fully support MCP's structuredContent field. + - id: download + tool: arxiv_download_paper + arguments: + paper_id: '{{(index (fromJson .steps.search.output.text).papers 0).id}}' + dependsOn: [search] + # Step 3: Read the downloaded paper content + - id: read + tool: arxiv_read_paper + arguments: + paper_id: '{{(index (fromJson .steps.search.output.text).papers 0).id}}' + dependsOn: [download] +``` + +**What's happening:** + +1. **Parameters**: Define the workflow inputs (`query` for the research topic) +2. **Step 1 (search)**: Calls `arxiv_search_papers` with the query from + parameters using template syntax `{{.params.query}}` +3. **Step 2 (download)**: Waits for search (`dependsOn: [search]`), then + downloads the paper. The `fromJson` function parses the JSON text returned by + the server, and `index` accesses the first paper's ID. +4. **Step 3 (read)**: Waits for download, then reads the paper content. + +When a client calls this composite tool, vMCP executes all three steps in +sequence and returns the paper content. + +## Structured content vs JSON text + +MCP servers can return data in two ways: + +- **Structured content**: Data is in `structuredContent` and can be accessed + directly: `{{.steps.stepid.output.field}}` +- **JSON text**: Data is returned as a JSON string in the `text` field and + requires parsing: `{{(fromJson .steps.stepid.output.text).field}}` + +The arxiv-mcp-server in this example uses JSON text, so we use `fromJson`. Check +your backend's response format to determine which approach to use. + +## Use cases + +### Incident investigation + +Gather data from multiple monitoring systems in parallel: + +```yaml title="VirtualMCPServer resource" +spec: + config: + 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 title="VirtualMCPServer resource" +spec: + config: + 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 backend MCP servers: + +```yaml title="VirtualMCPServer resource" +spec: + config: + compositeTools: + - name: security_scan_report + description: Run security scans and create consolidated report + parameters: + type: object + properties: + package_name: + type: string + ecosystem: + type: string + repo: + type: string + required: + - package_name + - ecosystem + - repo + steps: + - id: vulnerability_scan + tool: osv_query_vulnerability + arguments: + package_name: '{{.params.package_name}}' + ecosystem: '{{.params.ecosystem}}' + - 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: 'Vulnerability scan completed for {{.params.package_name}}' + dependsOn: [vulnerability_scan, secret_scan] + onError: + action: continue +``` + +## Workflow definition + +### Parameters + +Define input parameters using JSON Schema format: + +```yaml title="VirtualMCPServer resource" +spec: + config: + 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 title="VirtualMCPServer resource" +spec: + config: + 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 +``` + +The `tool` field specifies which MCP server tool to call. This depends on your +[conflict resolution strategy](./tool-aggregation.mdx#conflict-resolution-strategies) +and prefix format. For example, if you have a tool named `search` in an MCP +server named `arxiv`, and you're using the default prefix format, you would +reference it as `arxiv_search`. + +:::tip + +When using the `condition` field, downstream steps that reference the +conditional step's output may require +[default step outputs](#default-step-outputs) to handle cases where the +condition evaluates to false. + +::: + +### Elicitation (user prompts) + +Request input from users during workflow execution: + +```yaml title="VirtualMCPServer resource" +spec: + config: + compositeTools: + - name: + steps: + - id: approval + type: elicitation + message: 'Proceed with deployment?' + schema: + type: object + properties: + confirm: { type: boolean } + timeout: '5m' +``` + +### Error handling + +Configure behavior when steps fail: + +| Action | Description | +| ---------- | ------------------------------- | +| `abort` | Stop workflow immediately | +| `continue` | Log error, proceed to next step | +| `retry` | Retry with exponential backoff | + +```yaml title="VirtualMCPServer resource" +spec: + config: + compositeTools: + - name: + steps: + - id: + # ... other step config (tool, arguments, etc.) + onError: + action: retry + retryCount: 3 +``` + +:::tip + +When using `onError.action: continue`, downstream steps that reference this +step's output may require [default step outputs](#default-step-outputs) to +handle cases where the step fails. + +::: + +### Default step outputs + +When steps can be skipped (due to `condition` being false or +`onError.action: continue`), downstream steps that reference their outputs need +fallback values. Use `defaultResults` to provide these values. + +#### When defaultResults are required + +You must provide `defaultResults` when **both** of these conditions are true: + +1. A step can be skipped (has a `condition` field or `onError.action: continue`) +2. A downstream step references the skipped step's output in its arguments + +#### Configuration + +Define default values that match the expected output structure: + +```yaml title="VirtualMCPServer resource" +spec: + config: + compositeTools: + - name: optional_security_check + description: Run security scan with optional vulnerability check + parameters: + type: object + properties: + package_name: + type: string + ecosystem: + type: string + run_vuln_scan: + type: boolean + default: false + required: + - package_name + - ecosystem + steps: + # Step 1: Optional vulnerability scan + - id: vuln_scan + tool: osv_query_vulnerability + arguments: + package_name: '{{.params.package_name}}' + ecosystem: '{{.params.ecosystem}}' + condition: '{{.params.run_vuln_scan}}' + # highlight-start + defaultResults: + vulns: [] + # highlight-end + # Step 2: Create report using scan results + - id: create_report + tool: docs_create_document + arguments: + title: 'Security Report' + # This references vuln_scan output, so defaultResults are needed + body: 'Found {{len .steps.vuln_scan.output.vulns}} vulnerabilities' + dependsOn: [vuln_scan] +``` + +#### Continue on error example + +When using `onError.action: continue`, provide defaults for potential failures: + +```yaml title="VirtualMCPServer resource" +spec: + config: + compositeTools: + - name: multi_source_data + description: Gather data from multiple sources, continue on failures + steps: + # Step 1: Fetch from primary source (may fail) + - id: fetch_primary + tool: api_get_data + arguments: + source: 'primary' + onError: + action: continue + # highlight-start + defaultResults: + status: 'unavailable' + data: null + # highlight-end + # Step 2: Aggregate results + - id: aggregate + tool: processing_combine_data + arguments: + # Uses fetch_primary output even if it failed + primary: '{{.steps.fetch_primary.output.data}}' + dependsOn: [fetch_primary] +``` + +#### Validation + +vMCP validates `defaultResults` at configuration time: + +- **Missing defaults**: If a step can be skipped and downstream steps reference + its output, but `defaultResults` is not provided, vMCP returns a validation + error +- **Structure**: The `defaultResults` value can be any valid JSON type (object, + array, string, number, boolean, null) +- **No type checking**: vMCP does not verify that `defaultResults` match the + actual output structure—you must ensure they match the format your downstream + steps expect + +#### Example validation error + +```yaml +# This will fail validation +steps: + - id: conditional_step + tool: backend_fetch + condition: '{{.params.enabled}}' + # Missing defaultResults! + - id: use_result + tool: backend_process + arguments: + # References conditional_step output + data: '{{.steps.conditional_step.output.value}}' + dependsOn: [conditional_step] +``` + +**Error message:** + +```text +step 'conditional_step' can be skipped but is referenced by downstream steps +without defaultResults defined +``` + +## Template syntax + +Access workflow context in arguments: + +| Template | Description | +| --------------------------- | ------------------------------------------ | +| `{{.params.name}}` | Input parameter | +| `{{.steps.id.output}}` | Step output (map) | +| `{{.steps.id.output.text}}` | Text content from step output | +| `{{.steps.id.content}}` | Elicitation response content | +| `{{.steps.id.action}}` | Elicitation action (accept/decline/cancel) | + +### Template functions + +The following functions are available for use in templates: + +| Function | Description | Example | +| ---------- | -------------------------------- | -------------------------------------------- | +| `fromJson` | Parse a JSON string into a value | `{{(fromJson .steps.s1.output.text).field}}` | +| `json` | Encode a value as a JSON string | `{{json .steps.s1.output}}` | +| `quote` | Quote a string value | `{{quote .params.name}}` | +| `index` | Access array elements by index | `{{index .steps.s1.output.items 0}}` | + +All +[Go template built-in functions](https://pkg.go.dev/text/template#hdr-Functions) +are also supported (e.g., `len`, `eq`, `and`, `or`, `printf`). + +### Accessing step outputs + +When an MCP server returns structured content, you can access output fields +directly: + +```yaml +# Direct access when server supports structuredContent +result: '{{.steps.fetch.output.data}}' +items: '{{index .steps.search.output.results 0}}' +``` + +This is the simplest approach and works when the backend MCP server populates +the `structuredContent` field in its response. + +### Working with JSON text responses + +Some MCP servers return structured data as JSON text rather than using MCP's +`structuredContent` field. When this happens, use `fromJson` to parse it: + +```yaml +# Parse JSON text and access a nested field +paper_id: '{{(index (fromJson .steps.search.output.text).papers 0).id}}' +``` + +This pattern: + +1. Gets the text output: `.steps.search.output.text` +2. Parses it as JSON: `fromJson ...` +3. Accesses the `papers` array and gets the first element: `index ... 0` +4. Gets the `id` field: `.id` + +**How to tell which approach to use:** Call the backend tool directly and +inspect the response. If `structuredContent` contains your data fields, use +direct access. If `structuredContent` only has a `text` field containing JSON, +use `fromJson`. + +## Complete example + +A VirtualMCPServer with an inline composite tool using the +[arxiv-mcp-server](https://github.com/blazickjp/arxiv-mcp-server): + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: research-vmcp + namespace: toolhive-system +spec: + incomingAuth: + type: anonymous + config: + groupRef: research-tools + aggregation: + conflictResolution: prefix + conflictResolutionConfig: + prefixFormat: '{workload}_' + compositeTools: + - name: research_topic + description: Search arXiv for papers and read the top result + parameters: + type: object + properties: + query: + type: string + description: Research topic to search for + required: + - query + steps: + - id: search + tool: arxiv_search_papers + arguments: + query: '{{.params.query}}' + max_results: 1 + - id: download + tool: arxiv_download_paper + arguments: + paper_id: '{{(index (fromJson .steps.search.output.text).papers 0).id}}' + dependsOn: [search] + - id: read + tool: arxiv_read_paper + arguments: + paper_id: '{{(index (fromJson .steps.search.output.text).papers 0).id}}' + dependsOn: [download] + timeout: '5m' +``` + +> Note: The example above assumes you have: +> +> - An `MCPGroup` named `research-tools`. +> - An `arxiv-mcp-server` deployed as an `MCPServer` or `MCPRemoteProxy` +> resource that references the `research-tools` group. +> +> For a complete example of configuring MCP groups and backend servers, see the +> quickstart and tool aggregation guides. For complex, reusable workflows, +> create `VirtualMCPCompositeToolDefinition` resources and reference them with +> `spec.config.compositeToolRefs`: + +```yaml title="VirtualMCPServer resource" +spec: + config: + groupRef: my-tools + compositeToolRefs: + - name: my-reusable-workflow + - name: another-workflow +``` + +## Related information + +- [Configure vMCP servers](./configuration.mdx) diff --git a/versioned_docs/version-1.0/toolhive/guides-vmcp/configuration.mdx b/versioned_docs/version-1.0/toolhive/guides-vmcp/configuration.mdx new file mode 100644 index 00000000..3c2f6505 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-vmcp/configuration.mdx @@ -0,0 +1,300 @@ +--- +title: Configure vMCP servers +description: How to configure a Virtual MCP Server for common scenarios. +--- + +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.md#apiv1alpha1virtualmcpserver). + +## Create an MCPGroup + +Before creating a VirtualMCPServer, you need an +[MCPGroup](../reference/crd-spec.md#apiv1alpha1mcpgroup) to organize the backend +MCP servers. An MCPGroup is a logical container that groups related MCPServer +and MCPRemoteProxy 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 stafrt. Backend resources +reference this group using the `groupRef` field in their spec. + +## Add backends to a group + +vMCP supports two types of backends that can be added to an MCPGroup: + +### MCPServer (local containers) + +MCPServer resources run container-based MCP servers in your cluster: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: fetch + namespace: toolhive-system +spec: + groupRef: my-group # Reference to the MCPGroup + image: ghcr.io/stackloklabs/gofetch/server + transport: streamable-http +``` + +### MCPRemoteProxy (remote servers) + +MCPRemoteProxy resources proxy external remote MCP servers. They can be added to +an MCPGroup for discovery by vMCP: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRemoteProxy +metadata: + name: context7-proxy + namespace: toolhive-system +spec: + groupRef: my-group # Reference to the MCPGroup + remoteURL: https://mcp.context7.com/mcp + transport: streamable-http + port: 8080 + + # Validate incoming requests + oidcConfig: + type: inline + inline: + issuer: https://auth.company.com + audience: context7-proxy +``` + +:::caution[Current limitation] + +vMCP can discover MCPRemoteProxy backends in a group, but authentication between +vMCP and MCPRemoteProxy is not yet fully implemented. This limitation will be +addressed in a future release. See +[Proxy remote MCP servers](../guides-k8s/remote-mcp-proxy.mdx#use-with-virtual-mcp-server) +for details. + +::: + +For complete MCPRemoteProxy configuration options, see +[Proxy remote MCP servers](../guides-k8s/remote-mcp-proxy.mdx). + +## Create a VirtualMCPServer + +At minimum, a VirtualMCPServer requires a reference to an MCPGroup (via +`config.groupRef`) and an authentication type: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp + namespace: toolhive-system +spec: + config: + groupRef: my-group + incomingAuth: + type: anonymous # Disables authentication; do not use in production +``` + +The MCPGroup must exist in the same namespace and be in a Ready state before the +VirtualMCPServer can start. By default, vMCP automatically discovers and +aggregates all MCPServer and MCPRemoteProxy resources in the referenced group. +You can also define backends explicitly in the configuration (inline mode). See +[Backend discovery modes](./backend-discovery.mdx) for details on both +approaches. + +## Configure 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. + +## Expose the service + +Choose how to expose the vMCP endpoint. The Service resource is created +automatically on port 4483. + +```yaml title="VirtualMCPServer resource" +spec: + 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 +``` + +**Service types:** + +- **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) + +The Service is named `vmcp-`, where `` is from `metadata.name` in +the VirtualMCPServer resource. + +## Monitor status + +Check the VirtualMCPServer status to verify it's ready: + +```bash +kubectl get virtualmcpserver my-vmcp +``` + +Key status fields: + +| Field | Description | +| -------------------- | ------------------------------------------------ | +| `phase` | Current state (Pending, Ready, Degraded, Failed) | +| `url` | Service URL for client connections | +| `backendCount` | Number of discovered backend MCP servers | +| `discoveredBackends` | Details about each backend and its auth type | + +## Operational configuration + +### Health checks + +vMCP continuously monitors backend health to detect failures and route requests +appropriately. Health check behavior is configurable via the VirtualMCPServer +resource. + +#### Health check configuration + +Configure health monitoring in `spec.config.operational.failureHandling`: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp + namespace: toolhive-system +spec: + config: + groupRef: my-group + operational: + failureHandling: + # Health check interval (how often to check each backend) + # Default: 30s + healthCheckInterval: 30s + + # Health check timeout (max duration for a single check) + # Should be less than healthCheckInterval + # Default: 10s + healthCheckTimeout: 10s + + # Number of consecutive failures before marking unhealthy + # Default: 3 + unhealthyThreshold: 3 + + # How often to report status updates to Kubernetes + # Default: 30s + statusReportingInterval: 30s + incomingAuth: + type: anonymous +``` + +#### Circuit breaker configuration + +Circuit breakers prevent cascading failures by temporarily stopping requests to +consistently failing backends. For detailed configuration, behavior, and +troubleshooting, see [Failure handling](./failure-handling.mdx). + +To enable circuit breaker: + +```yaml +spec: + config: + operational: + failureHandling: + circuitBreaker: + enabled: true + failureThreshold: 5 # Number of failures before opening circuit + timeout: 60s # How long to wait before attempting recovery +``` + +### Timeouts + +Configure timeouts for backend requests: + +```yaml +spec: + config: + operational: + timeouts: + # Default timeout for all backend requests (default: 30s) + default: 30s + + # Per-workload timeout overrides + perWorkload: + slow-backend: 60s + fast-backend: 10s +``` + +:::note + +Health check timeouts are configured separately via +`failureHandling.healthCheckTimeout` (default: 10s), not via the `timeouts` +section. + +::: + +#### Remote workload health checks + +By default, health checks are: + +- **Always enabled** for local backends (MCPServer) +- **Disabled by default** for remote backends (MCPRemoteProxy) + +To enable health checks for remote workloads, set the +`TOOLHIVE_REMOTE_HEALTHCHECKS` environment variable in the vMCP pod: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp +spec: + podTemplateSpec: + spec: + containers: + - name: vmcp + env: + - name: TOOLHIVE_REMOTE_HEALTHCHECKS + value: 'true' +``` + +For detailed backend health monitoring, see +[Verify backend status](./backend-discovery.mdx#verify-backend-status) in the +Backend discovery guide. + +## Next steps + +- [Optimize tool discovery](./optimizer.mdx) by adding an `embeddingServerRef` + to reduce token usage across many backends +- Review [scaling and performance guidance](./scaling-and-performance.mdx) for + resource planning +- Discover your deployed MCP servers automatically using the + [Kubernetes registry](../guides-registry/configuration.mdx#kubernetes-registry) + feature in the ToolHive Registry Server + +## Related information + +- [VirtualMCPServer CRD specification](../reference/crd-spec.md#apiv1alpha1virtualmcpserver) +- [Introduction to vMCP](./intro.mdx) +- [Scaling and Performance](./scaling-and-performance.mdx) +- [Backend discovery modes](./backend-discovery.mdx) +- [Tool aggregation](./tool-aggregation.mdx) +- [Optimize tool discovery](./optimizer.mdx) +- [Composite tools](./composite-tools.mdx) +- [Authentication](./authentication.mdx) +- [Proxy remote MCP servers](../guides-k8s/remote-mcp-proxy.mdx) diff --git a/versioned_docs/version-1.0/toolhive/guides-vmcp/failure-handling.mdx b/versioned_docs/version-1.0/toolhive/guides-vmcp/failure-handling.mdx new file mode 100644 index 00000000..df6b533e --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-vmcp/failure-handling.mdx @@ -0,0 +1,417 @@ +--- +title: Failure handling +description: + Configure circuit breaker and partial failure modes to handle backend failures + gracefully. +--- + +Virtual MCP Server (vMCP) implements failure handling patterns to prevent +cascading failures and provide graceful degradation when backends become +unavailable. This guide covers circuit breaker configuration and partial failure +modes. + +:::tip + +For backend health status monitoring and the `/status` endpoint, see +[Backend discovery modes](./backend-discovery.mdx#verify-backend-status). + +::: + +## Overview + +When backends fail due to crashes, network issues, or rate limiting, vMCP +provides circuit breaker and partial failure modes to handle failures +gracefully: + +- **Circuit breaker**: Prevents cascading failures by immediately rejecting + requests to failing backends instead of waiting for timeouts +- **Partial failure modes**: Choose whether to fail entire requests or continue + with available backends +- **Automatic recovery**: Backends are automatically restored when they recover + +:::tip + +Enable circuit breaker for production environments where backends may experience +temporary failures (deployments, restarts, rate limits). For highly stable +backends, health checks alone may be sufficient. + +::: + +## Circuit breaker + +The circuit breaker tracks backend failures and transitions through three +states: + +1. **Closed** (normal operation): Requests pass through to the backend. Failures + are counted. +2. **Open** (failing state): After exceeding the failure threshold, the circuit + opens. Requests fail immediately without contacting the backend. +3. **Half-open** (recovery testing): After a timeout period, the circuit allows + exactly one test request through. While this request is in progress, all + other requests are rejected (circuit remains half-open). If the test + succeeds, the circuit closes immediately and normal operation resumes. If it + fails, the circuit reopens for another timeout period. + +```mermaid +stateDiagram-v2 + [*] --> Closed + Closed --> Open: Failure threshold exceeded + Open --> HalfOpen: Timeout elapsed + HalfOpen --> Closed: Request succeeds + HalfOpen --> Open: Request fails + Closed --> Closed: Success (reset count) +``` + +### Enable circuit breaker + +Configure circuit breaker in the VirtualMCPServer resource: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp + namespace: toolhive-system +spec: + config: + groupRef: my-group + # highlight-start + operational: + failureHandling: + healthCheckInterval: 30s + unhealthyThreshold: 3 + circuitBreaker: + enabled: true + failureThreshold: 5 + timeout: 60s + # highlight-end + incomingAuth: + type: anonymous +``` + +### Configuration options + +| Field | Description | Default | +| ------------------------- | ----------------------------------------------------- | ------- | +| `healthCheckInterval` | Time between health checks for each backend | `30s` | +| `unhealthyThreshold` | Consecutive failures before marking backend unhealthy | `3` | +| `healthCheckTimeout` | Maximum duration for a single health check | `10s` | +| `statusReportingInterval` | Interval for reporting status to Kubernetes | `30s` | +| **Circuit breaker** | | | +| `enabled` | Enable circuit breaker | `false` | +| `failureThreshold` | Number of failures before opening the circuit | `5` | +| `timeout` | Duration to wait before testing recovery | `60s` | + +:::note + +Circuit breaker is disabled by default. Health checks run independently of the +circuit breaker and mark backends as healthy/unhealthy based on +`unhealthyThreshold`. + +::: + +:::note[Two failure thresholds] + +vMCP uses two thresholds: + +- **`unhealthyThreshold`** (default: 3): Consecutive health check failures + before marking backend unhealthy +- **`failureThreshold`** (default: 5): Consecutive request failures before + opening circuit breaker + +Health checks detect failures during idle periods (max detection time: +`healthCheckInterval × unhealthyThreshold`). Circuit breaker provides fast +failure protection during active traffic. + +::: + +## Partial failure modes + +Configure how vMCP behaves when some backends are unavailable: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp + namespace: toolhive-system +spec: + config: + groupRef: my-group + operational: + failureHandling: + # highlight-next-line + partialFailureMode: best_effort + incomingAuth: + type: anonymous +``` + +### Modes + +- **`fail`** (default): Entire request fails if any required backend is + unavailable. Use when all backends must be operational. +- **`best_effort`**: Return results from healthy backends even if some fail. + Tools from failed backends are omitted from responses. Use for graceful + degradation. + +### Example: Best effort mode + +With `partialFailureMode: best_effort`, if the GitHub backend is down but Fetch +is healthy, the `tools/list` response only includes tools from healthy backends: + +```json +{ + "jsonrpc": "2.0", + "result": { + "tools": [{ "name": "fetch_url", "description": "Fetch URL content" }] + }, + "id": 1 +} +``` + +GitHub tools are omitted from the response because the circuit breaker is open. +The client doesn't see unavailable backend tools, preventing timeout errors when +attempting to call them. + +## Monitor circuit breaker status + +Check backend health and circuit state: + +```bash +kubectl get virtualmcpserver my-vmcp -n toolhive-system -o yaml +``` + +Status includes health information and circuit breaker state: + +```yaml +status: + phase: Degraded # Ready|Degraded if some backends unhealthy + backendCount: 2 # Only counts ready backends (fetch-mcp, jira-mcp) + discoveredBackends: + - name: github-mcp + status: unavailable + lastHealthCheck: '2025-02-09T10:29:45Z' + message: 'connection timeout' + circuitBreakerState: open # Circuit breaker state: closed|open|half-open + circuitLastChanged: '2025-02-09T10:28:30Z' # When circuit opened + consecutiveFailures: 8 # Current failure count + - name: fetch-mcp + status: ready + lastHealthCheck: '2025-02-09T10:30:05Z' + circuitBreakerState: closed + consecutiveFailures: 0 + - name: jira-mcp + status: ready + lastHealthCheck: '2025-02-09T10:30:03Z' + circuitBreakerState: half-open # Testing recovery + circuitLastChanged: '2025-02-09T10:30:00Z' + consecutiveFailures: 2 # Reduced after partial recovery +``` + +**Status fields:** + +- `status`: Backend health (ready, degraded, unavailable, unknown) +- `circuitBreakerState`: Circuit state (closed, open, half-open) - empty if + circuit breaker disabled +- `circuitLastChanged`: When the circuit breaker state last changed +- `consecutiveFailures`: Count of consecutive health check failures +- `message`: Additional information about backend status or errors + +The `/status` HTTP endpoint provides a simplified view: + +```bash +curl http://localhost:4483/status +``` + +```json +{ + "backends": [ + { + "name": "github-mcp", + "health": "unhealthy", + "transport": "sse", + "auth_type": "token_exchange" + }, + { + "name": "fetch-mcp", + "health": "healthy", + "transport": "streamable-http", + "auth_type": "unauthenticated" + } + ], + "healthy": false, + "version": "v1.2.3", + "group_ref": "my-group" +} +``` + +:::info + +The `/status` endpoint provides basic health information but does not include +circuit breaker state. For detailed circuit breaker information +(`circuitBreakerState`, `consecutiveFailures`, `circuitLastChanged`), use the +Kubernetes status shown above. See +[Backend discovery modes](./backend-discovery.mdx#verify-backend-status) for +more details on the `/status` endpoint. + +::: + +## Example configurations + +### Production with aggressive failure detection + +Detect failures quickly and fail fast: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: production-vmcp + namespace: toolhive-system +spec: + config: + groupRef: production-backends + operational: + failureHandling: + # Check every 10 seconds + healthCheckInterval: 10s + # Mark unhealthy after 2 failures (20 seconds) + unhealthyThreshold: 2 + healthCheckTimeout: 5s + # Open circuit after 3 failures + circuitBreaker: + enabled: true + failureThreshold: 3 + timeout: 30s + # Fail requests if any backend down + partialFailureMode: fail + incomingAuth: + type: oidc + oidc: + issuerRef: + name: my-issuer +``` + +### Development with best effort + +Continue with available backends: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: dev-vmcp + namespace: toolhive-system +spec: + config: + groupRef: dev-backends + operational: + failureHandling: + healthCheckInterval: 30s + unhealthyThreshold: 3 + circuitBreaker: + enabled: true + failureThreshold: 5 + timeout: 60s + # Continue with healthy backends + partialFailureMode: best_effort + incomingAuth: + type: anonymous +``` + +## Troubleshooting + +
+Circuit breaker opens too frequently + +If the circuit breaker is too sensitive: + +**Increase failure threshold:** + +```yaml +operational: + failureHandling: + circuitBreaker: + failureThreshold: 10 # Require more failures before opening +``` + +**Increase timeout:** + +```yaml +operational: + failureHandling: + circuitBreaker: + timeout: 120s # Give backends more time to recover +``` + +
+ +
+Backends not recovering automatically + +If backends stay unhealthy after recovering: + +1. **Test backend connectivity** + + Verify the backend MCP server is accessible from vMCP: + + ```bash + kubectl exec -n toolhive-system deployment/vmcp-my-vmcp -- \ + curl -v http://my-backend:8080/mcp + ``` + + The backend should respond with MCP protocol headers. + +2. **Increase circuit breaker timeout** + + ```yaml + operational: + failureHandling: + circuitBreaker: + timeout: 90s # Allow more time for full recovery + ``` + +3. **Review vMCP logs** + + ```bash + kubectl logs -n toolhive-system deployment/vmcp-my-vmcp + ``` + + Look for circuit breaker state transitions: + + ``` + WARN Circuit breaker for backend github-mcp OPENED (threshold exceeded) + INFO Circuit breaker for backend github-mcp CLOSED (recovery successful) + ``` + +
+ +
+Healthy backends marked unhealthy + +If backends are incorrectly marked unhealthy: + +**Increase health check timeout:** + +```yaml +operational: + failureHandling: + healthCheckTimeout: 20s # Allow slower responses +``` + +**Increase unhealthy threshold:** + +```yaml +operational: + failureHandling: + unhealthyThreshold: 5 # Allow more failures before marking unhealthy +``` + +
+ +## Related information + +- [Backend discovery modes](./backend-discovery.mdx) - Backend health status and + `/status` endpoint +- [Configuration guide](./configuration.mdx) +- [VirtualMCPServer CRD specification](../reference/crd-spec.md#apiv1alpha1virtualmcpserver) diff --git a/versioned_docs/version-1.0/toolhive/guides-vmcp/index.mdx b/versioned_docs/version-1.0/toolhive/guides-vmcp/index.mdx new file mode 100644 index 00000000..b6f898e7 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-vmcp/index.mdx @@ -0,0 +1,26 @@ +--- +title: Virtual MCP Server +description: Aggregate multiple MCP servers into a single unified endpoint. +--- + +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 vMCP + +- 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 + +## Get started + +- [Understanding Virtual MCP Server](../concepts/vmcp.mdx) - Learn what vMCP + does and when to use it +- [Quickstart: Virtual MCP Server](./quickstart.mdx) - Deploy your first vMCP + +## Contents + +import DocCardList from '@theme/DocCardList'; + + diff --git a/versioned_docs/version-1.0/toolhive/guides-vmcp/intro.mdx b/versioned_docs/version-1.0/toolhive/guides-vmcp/intro.mdx new file mode 100644 index 00000000..87c4f630 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-vmcp/intro.mdx @@ -0,0 +1,113 @@ +--- +title: Introduction to vMCP +description: Understand what Virtual MCP Server (vMCP) does and when to use it. +--- + +## Overview + +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. + +vMCP supports two types of backends: + +- **MCPServer**: Container-based MCP servers running in your cluster +- **MCPRemoteProxy**: Proxies to external remote MCP servers (such as Notion, + analytics platforms, or other SaaS MCP endpoints) + +:::note[MCPRemoteProxy support] + +vMCP can discover MCPRemoteProxy backends in a group, but authentication between +vMCP and MCPRemoteProxy is not yet fully implemented. MCPServer backends work +fully with vMCP. See +[Proxy remote MCP servers](../guides-k8s/remote-mcp-proxy.mdx#use-with-virtual-mcp-server) +for details on the current limitations. + +::: + +## Core capabilities + +- **Multi-server aggregation**: Connect to one endpoint instead of many, + including both local container-based servers and remote MCP proxies +- **Tool conflict resolution**: Automatic namespacing when backend MCP servers + have overlapping tool names +- **Centralized authentication**: Single sign-on with per-backend token exchange +- **Composite workflows**: Multi-step operations across backend MCP servers with + parallel execution, approval gates, and error handling +- **Tool optimization**: Replace all individual tool definitions with two + lightweight primitives (`find_tool` and `call_tool`) to reduce token usage and + improve tool selection. See [Optimize tool discovery](./optimizer.mdx) and the + underlying [concepts](../concepts/tool-optimization.mdx) + +## When to use vMCP + +### Good fit + +- You manage 5+ MCP servers (local or remote) +- You need cross-system workflows requiring coordination +- You have centralized authentication and authorization requirements +- You need reusable workflow definitions +- You want to aggregate external SaaS MCP servers with internal tools +- You want to reduce token usage and improve tool selection accuracy across many + backends with the [optimizer](./optimizer.mdx) + +### Not needed + +- You use a single MCP server +- You have simple, one-step operations +- You have no orchestration requirements + +## Architecture overview + +```mermaid +flowchart TB + Client[MCP Client] --> vMCP[Virtual MCP Server] + + subgraph LocalBackends[Local MCP Servers] + GitHub[GitHub MCP] + Fetch[Fetch MCP] + end + + subgraph RemoteBackends[Remote MCP Proxies] + Notion[Notion MCP] + Neon[Neon MCP] + end + + vMCP --> LocalBackends + vMCP --> RemoteBackends + + RemoteBackends -->|proxied| ExternalServices[External Services] +``` + +## How it works + +1. You define an MCPGroup (a resource that organizes related MCP servers) +2. Backend resources reference the group using `groupRef`: + - **MCPServer** resources for container-based MCP servers + - **MCPRemoteProxy** resources for external remote MCP servers +3. You create a VirtualMCPServer that references the group +4. The operator discovers all MCPServer and MCPRemoteProxy backends in the group + and aggregates their capabilities +5. Clients connect to the VirtualMCPServer endpoint and see a unified view of + all tools from both local and remote backends + +## Optimize tool discovery + +As the number of aggregated backends grows, clients receive a large number of +tool definitions that consume tokens and can degrade tool selection accuracy. +The vMCP optimizer addresses this by replacing all individual tool definitions +with two lightweight primitives (`find_tool` and `call_tool`) and using hybrid +semantic and keyword search to surface only the most relevant tools per request. +To enable the optimizer, add an `embeddingServerRef` to your VirtualMCPServer +resource. See [Optimize tool discovery](./optimizer.mdx) for the full setup +guide. + +## Related information + +- [Quickstart: Virtual MCP Server](./quickstart.mdx) +- [Understanding Virtual MCP Server](../concepts/vmcp.mdx) +- [Optimize tool discovery](./optimizer.mdx) +- [Scaling and Performance](./scaling-and-performance.mdx) +- [Proxy remote MCP servers](../guides-k8s/remote-mcp-proxy.mdx) diff --git a/versioned_docs/version-1.0/toolhive/guides-vmcp/optimizer.mdx b/versioned_docs/version-1.0/toolhive/guides-vmcp/optimizer.mdx new file mode 100644 index 00000000..fe159aaf --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-vmcp/optimizer.mdx @@ -0,0 +1,317 @@ +--- +title: Optimize tool discovery +description: + Enable the optimizer in vMCP to reduce token usage and improve tool selection + across aggregated backends. +--- + +When Virtual MCP Server (vMCP) aggregates many backend MCP servers, the total +number of tools exposed to clients can grow quickly. The optimizer addresses +this by filtering tools per request, reducing token usage and improving tool +selection accuracy. + +For the desktop/CLI approach using the MCP Optimizer container, see the +[MCP Optimizer tutorial](../tutorials/mcp-optimizer.mdx). This guide covers the +Kubernetes operator approach using VirtualMCPServer and EmbeddingServer CRDs. + +## Benefits + +- **Reduced token usage**: Only relevant tools are included in context, not the + entire toolset +- **Improved tool selection**: The right tools surface for each query. With + fewer tools to reason over, agents are more likely to choose correctly + +## How it works + +1. You send a prompt that requires tool assistance +2. The AI calls `find_tool` with keywords extracted from the prompt +3. vMCP performs hybrid semantic and keyword search across all backend tools +4. Only the most relevant tools (up to 8 by default) are returned +5. The AI calls `call_tool` to execute the selected tool, and vMCP routes the + request to the appropriate backend + +```mermaid +flowchart TB + subgraph vmcpGroup["VirtualMCPServer"] + direction TB + vmcp["vMCP (optimizer enabled)"] + end + subgraph embedding["EmbeddingServer"] + direction TB + tei["Text Embeddings Inference"] + end + subgraph backends["MCPGroup backends"] + direction TB + mcp1["MCP server"] + mcp2["MCP server"] + mcp3["MCP server"] + end + + client(["Client"]) <-- "find_tool / call_tool" --> vmcpGroup + vmcp <-. "semantic search" .-> embedding + vmcp <-. "discovers / routes" .-> backends +``` + +:::info[How search works internally] + +The optimizer uses an internal SQLite database for both keyword search (using +full-text search) and storing semantic vectors. Keyword search runs locally +against this database; semantic search uses vectors generated by an embedding +server. You can control how results from these two sources are blended — see the +[parameter reference](#parameter-reference) for details. + +::: + +## Quick start + +### Step 1: Create an EmbeddingServer + +Create an EmbeddingServer with default settings. This deploys a text embeddings +inference (TEI) server using the `BAAI/bge-small-en-v1.5` model: + +```yaml title="embedding-server.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: EmbeddingServer +metadata: + name: my-embedding + namespace: toolhive-system +spec: {} +``` + +:::tip + +Wait for the EmbeddingServer to reach the `Running` phase before proceeding. The +first startup may take a few minutes while the model downloads. + +```bash +kubectl get embeddingserver my-embedding -n toolhive-system -w +``` + +::: + +### Step 2: Add the embedding reference to VirtualMCPServer + +Update your existing VirtualMCPServer to include `embeddingServerRef`. **This is +the only change needed to enable the optimizer.** When you set +`embeddingServerRef`, the operator automatically enables the optimizer with +sensible defaults. You only need to add an explicit `optimizer` block if you +want to [tune the parameters](#tune-the-optimizer). + +```yaml title="VirtualMCPServer resource" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp + namespace: toolhive-system +spec: + # highlight-start + embeddingServerRef: + name: my-embedding + # highlight-end + config: + groupRef: my-group + incomingAuth: + type: anonymous +``` + +### Step 3: Verify + +Check that the VirtualMCPServer is ready: + +```bash +kubectl get virtualmcpserver my-vmcp -n toolhive-system +``` + +Look for `READY: True` in the output. Once ready, clients connecting to the vMCP +endpoint see only `find_tool` and `call_tool` instead of the full backend +toolset. + +## EmbeddingServer resource + +The EmbeddingServer CRD manages the lifecycle of a TEI server. An empty +`spec: {}` uses all defaults. The two most important fields you can customize +are: + +- **`model`**: The Hugging Face embedding model to use. The default + (`BAAI/bge-small-en-v1.5`) is the tested and recommended model. You can + substitute any embedding model available on Hugging Face — see the + [MTEB leaderboard](https://huggingface.co/spaces/mteb/leaderboard) to compare + options. +- **`image`**: The container image for + [text-embeddings-inference](https://github.com/huggingface/text-embeddings-inference) + (TEI). The default is the CPU-only image + (`ghcr.io/huggingface/text-embeddings-inference:cpu-latest`). Swap this for a + CUDA-enabled image if you have GPU nodes available. + +For the complete field reference, see the +[EmbeddingServer CRD specification](../reference/crd-spec.md#apiv1alpha1embeddingserver). + +:::warning[ARM64 compatibility] + +The default TEI CPU images depend on Intel MKL, which is x86_64-only. No +official ARM64 images exist yet. On ARM64 nodes (including Apple Silicon with +kind), you can run the amd64 image under emulation as a workaround. + +First, pull the amd64 image and load it into your cluster: + +```bash +docker pull --platform linux/amd64 \ + ghcr.io/huggingface/text-embeddings-inference:cpu-1.7 +kind load docker-image \ + ghcr.io/huggingface/text-embeddings-inference:cpu-1.7 +``` + +The `kind load` command is specific to kind. For other cluster distributions, +use the equivalent image-loading mechanism (for example, `ctr images import` for +containerd, or push the image to a registry your cluster can pull from). + +Then, pin the image in your EmbeddingServer so the operator uses the pre-pulled +tag instead of the default `cpu-latest`: + +```yaml title="embedding-server.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: EmbeddingServer +metadata: + name: my-embedding + namespace: toolhive-system +spec: + image: ghcr.io/huggingface/text-embeddings-inference:cpu-1.7 +``` + +Native ARM64 support is in progress upstream. Track the +[TEI GitHub repository](https://github.com/huggingface/text-embeddings-inference) +for updates. + +::: + +## Tune the optimizer + +To customize optimizer behavior, add the `optimizer` block under `spec.config` +in your VirtualMCPServer resource: + +```yaml title="VirtualMCPServer resource" +spec: + config: + groupRef: my-group + # highlight-start + optimizer: + embeddingServiceTimeout: 30s + maxToolsToReturn: 8 + hybridSearchSemanticRatio: '0.5' + semanticDistanceThreshold: '1.0' + # highlight-end +``` + +### Parameter reference + +| Parameter | Description | Default | +| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| `embeddingServiceTimeout` | HTTP request timeout for calls to the embedding service | `30s` | +| `maxToolsToReturn` | Maximum number of tools returned per search (1-50) | `8` | +| `hybridSearchSemanticRatio` | Balance between semantic and keyword search. `0.0` = all keyword, `1.0` = all semantic. Default gives equal weight to both. | `"0.5"` | +| `semanticDistanceThreshold` | Maximum distance from the search term for semantic results. `0` = identical, `2` = completely unrelated. Results beyond this threshold are filtered out. | `"1.0"` | + +:::note + +`hybridSearchSemanticRatio` and `semanticDistanceThreshold` are string-encoded +floats (for example, `"0.5"` not `0.5`). This is a Kubernetes CRD limitation, as +CRDs do not support float types portably. + +::: + +:::info[EmbeddingServer is always required] + +Even if you set `hybridSearchSemanticRatio` to `"0.0"` (all keyword search), the +optimizer still requires a configured EmbeddingServer. The EmbeddingServer won't +be used at runtime when the semantic ratio is `0.0`, but the configuration must +be present due to how the optimizer is wired internally. + +::: + +:::tip[Tuning guidance] + +The defaults are well-tested and work for most use cases. If you do need to +adjust them: + +- **Lower `semanticDistanceThreshold`** (for example, `"0.6"`) for higher + precision: only very close matches are returned +- **Raise `semanticDistanceThreshold`** (for example, `"1.4"`) for higher + recall: broader matches are included +- **Increase `maxToolsToReturn`** if the AI frequently cannot find the right + tool; decrease it to save tokens +- **Adjust `hybridSearchSemanticRatio`** toward `"1.0"` if tool names are not + descriptive, or toward `"0.0"` if exact keyword matching is more useful +- `semanticDistanceThreshold` filtering is applied before the `maxToolsToReturn` + cap. A low threshold can filter out candidates before the cap takes effect, so + you may need to raise the threshold if too few results are returned + +::: + +## Complete example + +This example shows a full configuration with all available options, including +high availability for the embedding server, persistent model caching, and tuned +optimizer parameters. + +The EmbeddingServer runs two replicas with resource limits and a persistent +volume for model caching, so restarts don't re-download the model: + +```yaml title="embedding-server-full.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: EmbeddingServer +metadata: + name: full-embedding + namespace: toolhive-system +spec: + replicas: 2 + resources: + requests: + cpu: '500m' + memory: '512Mi' + limits: + cpu: '2' + memory: '1Gi' + modelCache: + enabled: true + storageSize: 5Gi +``` + +The VirtualMCPServer uses a shorter embedding timeout (15s) because the +EmbeddingServer is co-located with low-latency access. Increase this value if +the embedding service is remote or under high load: + +```yaml title="vmcp-with-optimizer.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: full-vmcp + namespace: toolhive-system +spec: + embeddingServerRef: + name: full-embedding + config: + groupRef: my-tools + optimizer: + embeddingServiceTimeout: 15s + maxToolsToReturn: 10 + hybridSearchSemanticRatio: '0.6' + semanticDistanceThreshold: '0.8' + incomingAuth: + type: oidc + oidcConfig: + type: inline + inline: + issuer: https://auth.example.com + audience: vmcp-example +``` + +## Related information + +- [MCP Optimizer tutorial](../tutorials/mcp-optimizer.mdx) — desktop/CLI setup +- [Optimizing LLM context](../concepts/tool-optimization.mdx) — background on + tool filtering and context pollution +- [Configure vMCP servers](./configuration.mdx) +- [EmbeddingServer CRD specification](../reference/crd-spec.md#apiv1alpha1embeddingserver) +- [Virtual MCP Server overview](../concepts/vmcp.mdx) — conceptual overview of + vMCP +- [VirtualMCPServer CRD specification](../reference/crd-spec.md#apiv1alpha1virtualmcpserver) diff --git a/versioned_docs/version-1.0/toolhive/guides-vmcp/quickstart.mdx b/versioned_docs/version-1.0/toolhive/guides-vmcp/quickstart.mdx new file mode 100644 index 00000000..1f60db06 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-vmcp/quickstart.mdx @@ -0,0 +1,282 @@ +--- +title: 'Quickstart: Virtual MCP Server' +sidebar_label: Quickstart +description: + Learn how to aggregate multiple MCP servers into a single endpoint using + Virtual MCP Server. +--- + +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 + +- How to create an MCPGroup to organize backend servers +- How to deploy multiple MCPServers in a group +- How to create a VirtualMCPServer that aggregates them +- How tool conflict resolution works +- How to connect your AI client to the aggregated endpoint + +## Prerequisites + +Before starting this tutorial, make sure you have: + +- A Kubernetes cluster with the ToolHive operator installed (see + [Quickstart: Kubernetes Operator](../guides-k8s/quickstart.mdx)) +- `kubectl` configured to communicate with your cluster +- An MCP client (Visual Studio Code with Copilot is used in this tutorial) + +## Step 1: Create an MCPGroup + +First, create an MCPGroup to organize your backend MCP servers: + +```yaml title="mcpgroup.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPGroup +metadata: + name: demo-tools + namespace: toolhive-system +spec: + description: Demo group for vMCP aggregation +``` + +Apply the resource: + +```bash +kubectl apply -f mcpgroup.yaml +``` + +Verify the group was created: + +```bash +kubectl get mcpgroups -n toolhive-system +``` + +## Step 2: Deploy backend MCPServers + +Deploy two MCP servers that will be aggregated. Both reference the `demo-tools` +group in the `groupRef` field: + +```yaml {11,30} title="mcpservers.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: fetch + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/gofetch/server + transport: streamable-http + proxyPort: 8080 + mcpPort: 8080 + groupRef: demo-tools + resources: + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' +--- +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: osv + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/osv-mcp/server + transport: streamable-http + proxyPort: 8080 + mcpPort: 8080 + groupRef: demo-tools + resources: + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' +``` + +Apply the resources: + +```bash +kubectl apply -f mcpservers.yaml +``` + +Wait for both servers to be running: + +```bash +kubectl get mcpservers -n toolhive-system -w +``` + +You should see both servers with `Running` status before continuing. + +## Step 3: Create a VirtualMCPServer + +Create a VirtualMCPServer that aggregates both backends: + +```yaml title="virtualmcpserver.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: demo-vmcp + namespace: toolhive-system +spec: + # No incoming auth for development (anonymous access) + incomingAuth: + type: anonymous + + # Auto-discover auth config from backend MCPServers + outgoingAuth: + source: inline + # No default specified will use anonymous + + # Expose as ClusterIP (cluster-internal or exposed via Ingress/Gateway) + serviceType: ClusterIP + + config: + # Reference the MCPGroup containing fetch and osv servers + groupRef: demo-tools + # Tool aggregation with prefix strategy to avoid naming conflicts + aggregation: + conflictResolution: prefix + conflictResolutionConfig: + prefixFormat: '{workload}_' +``` + +Apply the resource: + +```bash +kubectl apply -f virtualmcpserver.yaml +``` + +Check the status: + +```bash +kubectl get virtualmcpservers -n toolhive-system +``` + +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 vMCP to +aggregate their tools. With the `prefix` conflict resolution strategy, all tools +are prefixed with the backend name. + +::: + +## Step 4: Verify the aggregation + +Check the discovered backends: + +```bash +kubectl describe virtualmcpserver demo-vmcp -n toolhive-system +``` + +Look for the `Discovered Backends` section in the status, which should show both +backends. + +## Step 5: Connect your client + +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 +``` + +Test the health endpoint: + +```bash +curl http://localhost:4483/health +``` + +You should see `{"status":"ok"}`. + +Add the port-forwarded vMCP endpoint as a remote server in ToolHive: + +```bash +thv run http://localhost:4483/mcp --name demo-vmcp +``` + +This registers the vMCP endpoint as a ToolHive-managed workload, which +automatically configures your registered MCP clients to connect to it. + +:::tip + +If you haven't set up client configuration yet, run `thv client setup` to +register your MCP clients. See +[Client configuration](../guides-cli/client-configuration.mdx) for more details. + +::: + +## Step 6: Test the aggregated tools + +Try asking your AI assistant questions that use the aggregated tools. Both tools +work through the same vMCP endpoint! + +## Step 7: Clean up + +Delete the resources when you're done: + +```bash +kubectl delete virtualmcpserver demo-vmcp -n toolhive-system +kubectl delete mcpserver fetch osv -n toolhive-system +kubectl delete mcpgroup demo-tools -n toolhive-system +``` + +## What's next? + +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.mdx) + +## Troubleshooting + +
+VirtualMCPServer stuck in Pending + +Check that the MCPGroup exists and backend MCPServers are running: + +```bash +kubectl get mcpgroups,mcpservers -n toolhive-system +``` + +Check the operator logs: + +```bash +kubectl logs -n toolhive-system -l app.kubernetes.io/name=toolhive-operator +``` + +
+ +
+Only some tools appearing + +Verify both backends are discovered: + +```bash +kubectl get virtualmcpserver demo-vmcp -n toolhive-system -o jsonpath='{.status.discoveredBackends[*].name}' +``` + +Check backend health in the status: + +```bash +kubectl describe virtualmcpserver demo-vmcp -n toolhive-system +``` + +
diff --git a/versioned_docs/version-1.0/toolhive/guides-vmcp/scaling-and-performance.mdx b/versioned_docs/version-1.0/toolhive/guides-vmcp/scaling-and-performance.mdx new file mode 100644 index 00000000..559dec02 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-vmcp/scaling-and-performance.mdx @@ -0,0 +1,88 @@ +--- +title: Scaling and Performance +description: How to scale Virtual MCP Server deployments vertically and horizontally. +--- + +This guide explains how to scale Virtual MCP Server (vMCP) deployments. + +## Vertical scaling + +Vertical scaling (increasing CPU/memory per instance) is the simplest approach +and works for all use cases, including stateful backends. + +To increase resources, configure `podTemplateSpec` in your VirtualMCPServer: + +```yaml +spec: + podTemplateSpec: + spec: + containers: + - name: vmcp + resources: + requests: + cpu: '500m' + memory: 512Mi + limits: + cpu: '1' + memory: 1Gi +``` + +Vertical scaling is recommended as the starting point for most deployments. + +## Horizontal scaling + +Horizontal scaling (adding more replicas) can improve availability and handle +higher request volumes. + +### How to scale horizontally + +The VirtualMCPServer CRD does not have a `replicas` field. The operator creates +a Deployment named `vmcp-` (where `` is your VirtualMCPServer name) +with 1 replica and preserves the replicas count, allowing you to manage scaling +separately. + +**Option 1: Manual scaling** + +```bash +kubectl scale deployment vmcp- -n --replicas=3 +``` + +**Option 2: Autoscaling with HPA** + +```bash +kubectl autoscale deployment vmcp- -n \ + --min=2 --max=5 --cpu-percent=70 +``` + +### When horizontal scaling is challenging + +Horizontal scaling works well for **stateless backends** (fetch, search, +read-only operations) where sessions can be resumed on any instance. + +However, **stateful backends** make horizontal scaling difficult: + +- **Stateful backends** (Playwright browser sessions, database connections, file + system operations) require requests to be routed to the same vMCP instance + that established the session +- Session resumption may not work reliably for stateful backends + +The `VirtualMCPServer` CRD includes a `sessionAffinity` field that controls how +the Kubernetes Service routes repeated client connections. By default, it uses +`ClientIP` affinity, which routes connections from the same client IP to the +same pod. You can configure this using the `sessionAffinity` field: + +```yaml +spec: + sessionAffinity: ClientIP # default +``` + +For stateful backends, vertical scaling or dedicated vMCP instances per team/use +case are recommended instead of horizontal scaling. + +## Related information + +- [Introduction to vMCP](./intro.mdx) +- [Configure health checks](./configuration.mdx#health-checks) +- [Backend discovery modes](./backend-discovery.mdx) +- [Telemetry and metrics](./telemetry-and-metrics.mdx) +- [VirtualMCPServer CRD specification](../reference/crd-spec.md#apiv1alpha1virtualmcpserver) diff --git a/versioned_docs/version-1.0/toolhive/guides-vmcp/telemetry-and-metrics.mdx b/versioned_docs/version-1.0/toolhive/guides-vmcp/telemetry-and-metrics.mdx new file mode 100644 index 00000000..6e54d6bf --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-vmcp/telemetry-and-metrics.mdx @@ -0,0 +1,176 @@ +--- +title: Telemetry and metrics +description: How to enable OpenTelemetry traces and metrics for Virtual MCP Server. +--- + +Virtual MCP Server (vMCP) provides comprehensive observability through +OpenTelemetry instrumentation. You can export traces and metrics to monitor +backend operations and workflow executions. + +## Telemetry types + +vMCP supports two types of telemetry: + +- **Traces**: Track requests across vMCP and its backends, showing the full path + of tool calls, resource reads, and workflow executions +- **Metrics**: Counters and histograms for backend request rates, error rates, + and latency distributions + +For general ToolHive observability concepts including trace structure and +metrics, see the [observability overview](../concepts/observability.mdx). + +## Enable telemetry + +Configure telemetry in the VirtualMCPServer resource using the +[`spec.config.telemetry` field](../reference/crd-spec.md#toolhivestacklokdevtelemetry): + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp + namespace: toolhive-system +spec: + config: + groupRef: my-group + # highlight-start + telemetry: + endpoint: 'otel-collector:4318' + serviceName: 'my-vmcp' + insecure: true + tracingEnabled: true + samplingRate: '0.05' + metricsEnabled: true + enablePrometheusMetricsPath: true + # highlight-end + incomingAuth: + type: anonymous +``` + +### Configuration options + +| Field | Description | Default | +| ----------------------------- | --------------------------------------- | --------------------- | +| `endpoint` | OTLP collector endpoint (hostname:port) | - | +| `serviceName` | Service name in traces and metrics | VirtualMCPServer name | +| `tracingEnabled` | Enable tracing | `false` | +| `metricsEnabled` | Enable OTLP metrics export | `false` | +| `samplingRate` | Trace sampling rate (0.0-1.0) | `"0.05"` | +| `insecure` | Use HTTP instead of HTTPS | `false` | +| `enablePrometheusMetricsPath` | Expose `/metrics` endpoint | `false` | + +## Export to observability backends + +### Export to Jaeger via OpenTelemetry Collector + +Deploy an OpenTelemetry Collector configured to export to Jaeger: + +```yaml title="otel-collector-config.yaml" +receivers: + otlp: + protocols: + http: + endpoint: 0.0.0.0:4318 + +processors: + batch: + timeout: 10s + send_batch_size: 1024 + +exporters: + otlp/jaeger: + endpoint: jaeger:4317 + tls: + insecure: true + +service: + pipelines: + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp/jaeger] +``` + +Then configure vMCP to send telemetry to the collector: + +```yaml +spec: + config: + telemetry: + endpoint: 'otel-collector:4318' + serviceName: 'production-vmcp' + tracingEnabled: true + metricsEnabled: true + insecure: true +``` + +## Metrics collection + +vMCP supports two methods for collecting metrics: + +- **Push via OpenTelemetry**: Set `metricsEnabled: true` to push metrics to your + OTel Collector via OTLP +- **Pull via Prometheus**: Set `enablePrometheusMetricsPath: true` to expose a + `/metrics` endpoint on the vMCP service port (4483) for Prometheus to scrape + +### Backend metrics + +These metrics track requests to individual MCP server backends: + +| Metric | Type | Description | +| ----------------------------------------- | --------- | ----------------------------------------------------------------------------- | +| `toolhive_vmcp_backends_discovered` | Gauge | Number of backends discovered | +| `toolhive_vmcp_backend_requests` | Counter | Total requests per backend | +| `toolhive_vmcp_backend_errors` | Counter | Total errors per backend | +| `toolhive_vmcp_backend_requests_duration` | Histogram | Duration of backend requests | +| `mcp.client.operation.duration` | Histogram | MCP client operation duration (`mcp_client_operation_duration` on `/metrics`) | + +### Workflow metrics + +These metrics track workflow execution across backends: + +| Metric | Type | Description | +| ----------------------------------- | --------- | ------------------------------- | +| `toolhive_vmcp_workflow_executions` | Counter | Total workflow executions | +| `toolhive_vmcp_workflow_errors` | Counter | Total workflow execution errors | +| `toolhive_vmcp_workflow_duration` | Histogram | Duration of workflow executions | + +### Optimizer metrics + +When the vMCP optimizer is enabled, these metrics track tool-finding and +tool-calling performance: + +| Metric | Type | Description | +| ----------------------------------------------- | --------- | --------------------------------------- | +| `toolhive_vmcp_optimizer_find_tool_requests` | Counter | Total FindTool calls | +| `toolhive_vmcp_optimizer_find_tool_errors` | Counter | Total FindTool errors | +| `toolhive_vmcp_optimizer_find_tool_duration` | Histogram | Duration of FindTool calls | +| `toolhive_vmcp_optimizer_find_tool_results` | Histogram | Number of tools returned per call | +| `toolhive_vmcp_optimizer_token_savings_percent` | Histogram | Token savings percentage per call | +| `toolhive_vmcp_optimizer_call_tool_requests` | Counter | Total CallTool calls | +| `toolhive_vmcp_optimizer_call_tool_errors` | Counter | Total CallTool errors | +| `toolhive_vmcp_optimizer_call_tool_not_found` | Counter | CallTool calls where tool was not found | +| `toolhive_vmcp_optimizer_call_tool_duration` | Histogram | Duration of CallTool calls | + +## Distributed tracing + +vMCP creates client-side spans for backend operations with the following span +names: + +- `tools/call ` - Tool calls to backends +- `resources/read` - Resource reads from backends +- `prompts/get ` - Prompt retrieval from backends +- `list_capabilities` - Backend capability discovery + +Each span includes attributes for the target backend (`target.workload_id`, +`target.workload_name`, `target.base_url`) and the relevant MCP attributes +(`mcp.method.name`, `gen_ai.tool.name`, `mcp.resource.uri`). + +## Related information + +- [Observability concepts](../concepts/observability.mdx) - Overview of + ToolHive's observability architecture +- [Kubernetes telemetry guide](../guides-k8s/telemetry-and-metrics.mdx) - + Telemetry for MCPServer resources +- [OpenTelemetry tutorial](../integrations/opentelemetry.mdx) - Set up a local + observability stack diff --git a/versioned_docs/version-1.0/toolhive/guides-vmcp/tool-aggregation.mdx b/versioned_docs/version-1.0/toolhive/guides-vmcp/tool-aggregation.mdx new file mode 100644 index 00000000..728d6915 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/guides-vmcp/tool-aggregation.mdx @@ -0,0 +1,244 @@ +--- +title: Tool aggregation and conflict resolution +description: + How vMCP aggregates tools from multiple backend MCP servers and resolves + naming conflicts. +--- + +When aggregating multiple MCP servers, tool name conflicts can occur when +different backend servers expose tools with the same name. Virtual MCP Server +(vMCP) provides strategies to resolve these conflicts automatically. + +## Overview + +vMCP discovers tools from all backend MCPServers in the referenced group and +presents them as a unified set to clients. When two backend MCP servers 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. + +:::tip + +When aggregating many backends, the total number of exposed tools can grow +quickly. Consider enabling the [optimizer](./optimizer.mdx) to reduce token +usage and improve tool selection accuracy. + +::: + +## Conflict resolution strategies + +### Prefix strategy (default) + +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 title="VirtualMCPServer resource" +spec: + config: + aggregation: + conflictResolution: prefix + conflictResolutionConfig: + prefixFormat: '{workload}_' +``` + +**Prefix format options:** + +| Format | Example result | +| ------------- | --------------------- | +| `{workload}` | `githubcreate_issue` | +| `{workload}_` | `github_create_issue` | +| `{workload}.` | `github.create_issue` | + +**Example:** + +With backend servers `github` and `jira`, both exposing `create_issue`: + +- GitHub's tool becomes `github_create_issue` +- Jira's tool becomes `jira_create_issue` + +### Priority strategy + +When multiple backend MCP servers 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 backend servers. + +```yaml title="VirtualMCPServer resource" +spec: + config: + aggregation: + conflictResolution: priority + conflictResolutionConfig: + 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 MCP server for specific tools +and want to hide duplicates. + +:::warning + +The priority strategy drops tools from lower-priority backend servers. Ensure +this is the intended behavior before using in production. + +::: + +### Manual strategy + +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 title="VirtualMCPServer resource" +spec: + config: + aggregation: + conflictResolution: manual + tools: + - workload: github + overrides: + create_issue: + name: gh_create_issue + - workload: jira + overrides: + create_issue: + name: jira_ticket +``` + +**When to use:** Production deployments where you want explicit control over +tool names. + +## Tool filtering + +Use filters to expose only specific tools from a backend MCP server, excluding +all others. This is useful for reducing the tool surface area presented to LLM +clients or removing unnecessary tools: + +```yaml title="VirtualMCPServer resource" +spec: + config: + aggregation: + tools: + - workload: github + filter: ['create_issue', 'list_issues', 'get_issue'] +``` + +Only the listed tools are included; all others from that backend MCP server are +excluded. + +## Tool overrides + +Use overrides to customize tool names and descriptions without modifying backend +MCP server configurations. This is useful for disambiguating similarly-named +tools or providing more context to LLM clients: + +```yaml title="VirtualMCPServer resource" +spec: + config: + aggregation: + tools: + - workload: github + overrides: + create_issue: + name: gh_new_issue + description: 'Create a new GitHub issue in the repository' +``` + +:::info + +You can also reference an `MCPToolConfig` resource using `toolConfigRef` instead +of inline filter and overrides. This feature is currently in development. + +::: + +## Combine filters and overrides + +You can combine filtering and overrides for fine-grained control: + +```yaml title="VirtualMCPServer resource" +spec: + config: + aggregation: + conflictResolution: prefix + conflictResolutionConfig: + prefixFormat: '{workload}_' + tools: + - workload: github + filter: ['create_issue', 'list_issues'] + overrides: + create_issue: + description: 'Create a GitHub issue (engineering team)' + - workload: jira + filter: ['create_issue', 'search_issues'] +``` + +## Example: Aggregating multiple MCP servers + +This example shows two MCP servers (fetch and osv) aggregated with prefix-based +conflict resolution: + +```yaml +# MCPGroup to organize backend servers +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPGroup +metadata: + name: demo-tools + namespace: toolhive-system +spec: + description: Demo group for tool aggregation +--- +# First backend: fetch server +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: fetch + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/gofetch/server + transport: streamable-http + proxyPort: 8080 + mcpPort: 8080 + groupRef: demo-tools +--- +# Second backend: osv server +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: osv + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/osv-mcp/server + transport: streamable-http + proxyPort: 8080 + mcpPort: 8080 + groupRef: demo-tools +--- +# VirtualMCPServer aggregating both backends +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: demo-vmcp + namespace: toolhive-system +spec: + incomingAuth: + type: anonymous + config: + groupRef: demo-tools + aggregation: + conflictResolution: prefix + conflictResolutionConfig: + prefixFormat: '{workload}_' +``` + +With this configuration, tools from each backend are prefixed: + +- `fetch_*` tools from the fetch server +- `osv_*` tools from the osv server + +## Related information + +- [VirtualMCPServer configuration reference](./configuration.mdx) +- [Optimize tool discovery](./optimizer.mdx) +- [Customize MCP server tools](../guides-k8s/customize-tools.mdx) diff --git a/versioned_docs/version-1.0/toolhive/index.mdx b/versioned_docs/version-1.0/toolhive/index.mdx new file mode 100644 index 00000000..c32cae3d --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/index.mdx @@ -0,0 +1,237 @@ +--- +title: Introduction +hide_title: true +description: ToolHive helps you run and manage MCP servers easily and securely. +--- + +import useBaseUrl from '@docusaurus/useBaseUrl'; +import ThemedImage from '@theme/ThemedImage'; + + +
+ +--- + + + +# What is ToolHive? + +ToolHive is an enterprise-grade open source (Apache 2.0) platform for running +and managing Model Context Protocol (MCP) servers. + +New to the Model Context Protocol? Start with the +[MCP primer](./concepts/mcp-primer.mdx). + +## Choose your path + +
+
+ +### Desktop app + +Run MCP servers with one click. The easiest way to get started for individual +developers. + +**[Get started with the UI →](./guides-ui/quickstart.mdx)** + +
+
+ +### CLI + +For power users and automation. Full control over MCP servers from the command +line. + +**[Get started with the CLI →](./guides-cli/quickstart.mdx)** + +
+
+ +### Kubernetes + +For teams and enterprises. Deploy and manage MCP servers at scale with the +ToolHive Operator. + +**[Get started with K8s →](./guides-k8s/quickstart.mdx)** + +
+
+ +## ToolHive components + +ToolHive includes everything you need to use MCP servers in production. It's +made up of four key components: the Runtime, Registry Server, Gateway, and +Portal. + + + +### Runtime + +The ToolHive Runtime is the core component that runs MCP servers in isolated +containers. It provides a secure and scalable environment for deploying MCP +servers, with features like fine-grained permissions, network access controls, +and secret management. + +The ToolHive Runtime is available in three different editions to suit different +use cases: + +- [**ToolHive UI**](./guides-ui/index.mdx): A desktop application for individual + developers to run and manage MCP servers locally. It provides a user-friendly + interface to discover, deploy, and manage MCP servers. + +- [**ToolHive CLI**](./guides-cli/index.mdx): A command line interface to deploy + and manage MCP servers on your local machine or in development environments. + It allows quick deployment of MCP servers and supports advanced features like + telemetry and fine-grained authorization policies. + +- [**ToolHive Kubernetes Operator**](./guides-k8s/index.mdx): A Kubernetes + operator for teams and enterprises to run and manage MCP servers in multi-user + environments. It provides centralized management, security controls, and + integration with existing infrastructure. + +### Registry Server + +The [**ToolHive Registry Server**](./guides-registry/index.mdx) is an +implementation of the official +[MCP Registry API](https://github.com/modelcontextprotocol/registry/blob/main/docs/reference/api/generic-registry-api.md). +Curate a catalog of trusted servers from multiple backend sources for users to +quickly discover and deploy. It can be deployed standalone or as part of the +ToolHive Operator. + +### Gateway + +Implemented in the ToolHive Kubernetes Operator as the +[**Virtual MCP Server (vMCP)**](./guides-vmcp/index.mdx), the ToolHive Gateway +is a secure proxy for MCP server connectivity, aggregation, and orchestration. +It's available as part of the ToolHive Operator for Kubernetes deployments. + +### Portal + +The ToolHive Portal is how users discover and install MCP servers. It's +available as a cross-platform desktop app (the ToolHive UI) and an experimental +[web-based frontend](https://github.com/stacklok/toolhive-cloud-ui) to the +ToolHive Registry Server. + +### Better together + +ToolHive components work together to provide a seamless experience for both +admins and users: + +1. **Admins** curate and organize MCP servers in the **Registry**, configuring + access and policies. +2. **Users** discover and request MCP servers from the **Portal**, and ToolHive + orchestrates installation and access. +3. **Runtime** securely deploys and manages MCP servers across local and cloud + environments, integrating seamlessly with existing SDLC workflows, exporting + analytics, and enforcing fine-grained access control. +4. **Gateway** handles all inbound traffic, secures context and credentials, + optimizes tool selection, and applies organizational policies. + +## Why ToolHive? + +We want to help you get real value from MCP servers. While there are plenty of +tools to help you quickly build MCP servers, the obstacles to using those +servers effectively are largely operational. ToolHive addresses the runtime, +security, and other considerations necessary to use MCP servers with confidence +and in production. + +We address those considerations with proven, familiar technologies like +containers and Kubernetes. + +ToolHive lets you run any MCP server, regardless of its underlying technology +stack, even when the original authors didn't provide container images. We +containerize the MCP server and let you use a simple CLI or Kubernetes to manage +MCP deployments at any scale. Coordinate all your MCP servers from one place +with sensible security controls and container-native simplicity. + +## Key features + +ToolHive includes a range of capabilities to help you run and manage MCP servers +effectively: + +**Runtime:** + +- Run local MCP servers instantly from the built-in registry of vetted MCP + servers, any Docker container, or directly from package managers +- Deploy MCP servers in the cloud via Kubernetes for enterprise scalability +- Proxy remote MCP servers securely for unified management +- Apply fine-grained permissions and network access filtering +- Protect sensitive data with built-in secrets management and enterprise OAuth + integrations +- Leverage OpenTelemetry and Prometheus for observability and audit logging + +**Registry Server:** + +- Curate a catalog of trusted MCP servers from multiple sources: the official + MCP Registry, other public registries, and custom files +- Group servers based on role or use case +- Manage your registry with an API-driven interface +- Preset configurations and permissions for a frictionless user experience + +**Gateway:** + +- Orchestrate multiple tools with a deterministic workflow engine +- Centralize control of security policy, authentication, authorization, + auditing, etc. +- Customize and filter tools and descriptions to improve performance and reduce + token usage + +**Portal:** + +- Cross-platform [desktop app](https://github.com/stacklok/toolhive-studio) and + browser-based [cloud UI](https://github.com/stacklok/toolhive-cloud-ui) +- Make it easy for end users to discover and deploy MCP servers +- Install MCP servers with a single click +- Connect with local clients like Claude Desktop, Cursor, VS Code, and many more + +## Additional resources + +Join the Stacklok [Discord community](https://discord.gg/stacklok) to connect +with other ToolHive users, ask questions, and share your experiences. + +Source code and issue tracking: + +- ToolHive CLI & K8s Operator + - [GitHub repository](https://github.com/stacklok/toolhive) + - [Issue tracker](https://github.com/stacklok/toolhive/issues) +- ToolHive UI + - [GitHub repository](https://github.com/stacklok/toolhive-studio) + - [Issue tracker](https://github.com/stacklok/toolhive-studio/issues) +- ToolHive Registry Server + - [GitHub repository](https://github.com/stacklok/toolhive-registry-server) + - [Issue tracker](https://github.com/stacklok/toolhive-registry-server/issues) +- ToolHive Cloud UI (experimental) + - [GitHub repository](https://github.com/stacklok/toolhive-cloud-ui) + - [Issue tracker](https://github.com/stacklok/toolhive-cloud-ui/issues) diff --git a/versioned_docs/version-1.0/toolhive/integrations/aws-sts.mdx b/versioned_docs/version-1.0/toolhive/integrations/aws-sts.mdx new file mode 100644 index 00000000..a09ed8db --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/integrations/aws-sts.mdx @@ -0,0 +1,663 @@ +--- +title: AWS STS authentication for the AWS MCP Server +description: + Learn how to centralize AWS credential management by using ToolHive to + exchange OIDC tokens for temporary AWS credentials via STS. +--- + +This tutorial shows you how to use ToolHive as an authentication proxy for the +[AWS MCP Server](https://docs.aws.amazon.com/aws-mcp/). Developers sign in +through their company identity provider, and ToolHive exchanges their OIDC token +for temporary AWS credentials via +[`AssumeRoleWithWebIdentity`](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html). + +:::info[Prerequisites] + +Before starting this tutorial, ensure you have: + +- A Kubernetes cluster with the ToolHive Operator installed (see the + [Kubernetes quickstart guide](../guides-k8s/quickstart.mdx)) +- `kubectl` configured to access your cluster +- The + [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) + installed and configured with an AWS account that has permissions to create + IAM roles, policies, and OIDC identity providers +- An OIDC identity provider (such as Okta, Auth0, Microsoft Entra ID, or + Keycloak) that your users authenticate with +- Basic familiarity with AWS IAM concepts and OIDC + +::: + +## Overview + +The following diagram shows how ToolHive processes each request: + +```mermaid +flowchart LR + subgraph client["Client"] + user["User"] + end + + subgraph gateway["ToolHive"] + authn["OIDC Auth"] + mapper["Role Mapper"] + sts["STS Exchange"] + signer["SigV4 Signer"] + end + + subgraph aws["AWS"] + stsapi["AWS STS"] + mcp["AWS MCP Server"] + end + + user -->|"JWT"| authn + authn --> mapper + mapper -->|"Selected Role ARN"| sts + sts <-->|"AssumeRoleWithWebIdentity"| stsapi + sts --> signer + signer -->|"SigV4 Signed Request"| mcp +``` + +1. The client sends a request with an OIDC token from your identity provider. +2. ToolHive validates the token against your OIDC provider's JWKS endpoint. +3. The **role mapper** inspects JWT claims (such as `groups`) and selects an IAM + role based on your configured mappings. +4. ToolHive calls AWS STS `AssumeRoleWithWebIdentity` to exchange the OIDC token + for temporary AWS credentials. +5. The **SigV4 signer** signs the outgoing request with the temporary + credentials. +6. The signed request is forwarded to the AWS MCP Server. + +The AWS MCP Server acts as the access point that allows AI assistants to connect +to different AWS services. Compared to configuring AWS credentials directly, +ToolHive adds: + +- **Company IdP integration:** Developers authenticate using company SSO. + ToolHive acquires short-lived, properly scoped credentials on their behalf. No + AWS CLI configuration is required, and AWS credentials are never stored on + developers' machines. +- **Fine-grained authorization:** Control which MCP tools a user can invoke. + Cedar policies are evaluated on every request, using claims from the incoming + token to make access decisions. +- **Observability, metrics, and auditing:** ToolHive provides OpenTelemetry + tracing, Prometheus metrics, and audit logs that correlate user identity + across the request flow. + +## Step 1: Register your identity provider with AWS IAM + +Create an OIDC identity provider in AWS IAM so that AWS STS trusts tokens from +your identity provider. + +```bash +aws iam create-open-id-connect-provider \ + --url https:// \ + --client-id-list +``` + +Replace the placeholders: + +- `` - your identity provider's issuer identifier without the + `https://` scheme. Include any path components (for example, + `dev-123456.okta.com/oauth2/default`) +- `` - the audience claim in your OIDC tokens that + identifies this proxy (for example, `toolhive-aws-proxy`) + +:::info[What's happening?] + +This step establishes a trust relationship between AWS and your identity +provider. When ToolHive later calls `AssumeRoleWithWebIdentity`, AWS STS +verifies the OIDC token signature against keys published by your identity +provider. Without this trust relationship, AWS rejects the token exchange. + +::: + +## Step 2: Create IAM roles and policies + +Create IAM roles that ToolHive assumes on behalf of your users. Each role +defines what AWS permissions a user gets when their JWT claims match a role +mapping. + +### Understanding the AWS MCP Server permission model + +AWS MCP Server authorization works in two layers: + +1. **MCP layer** (`aws-mcp:*` actions) - controls which categories of MCP tools + the user can invoke +2. **AWS service layer** (e.g., `s3:*`, `ec2:*`) - controls what the + `aws___call_aws` tool can actually do when it makes AWS API calls + +The `aws-mcp` namespace defines three actions: + +- `InvokeMcp` - required to connect and discover available tools +- `CallReadOnlyTool` - search documentation, list regions, get CLI suggestions + (most tools) +- `CallReadWriteTool` - execute real AWS API calls via the `aws___call_aws` tool + (requires additional service permissions) + +### Default role + +Create a role with minimal permissions. This is the fallback when no specific +role mapping matches. It needs two policy documents. + +The trust policy allows AWS STS to accept tokens from your OIDC provider. The +`Federated` principal identifies your registered provider, and the `aud` +condition rejects tokens meant for other services: + +```json title="default-mcp-trust-policy.json" +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Federated": "arn:aws:iam:::oidc-provider/" + }, + "Action": "sts:AssumeRoleWithWebIdentity", + "Condition": { + "StringEquals": { + ":aud": "" + } + } + } + ] +} +``` + +The permission policy grants read-only MCP access. Users can search AWS +documentation and get suggestions, but cannot execute AWS API calls: + +```json title="default-mcp-permissions.json" +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": ["aws-mcp:InvokeMcp", "aws-mcp:CallReadOnlyTool"], + "Resource": "*" + } + ] +} +``` + +Create the role with the trust policy and attach the permission policy: + +```bash +aws iam create-role \ + --role-name DefaultMCPRole \ + --assume-role-policy-document file://default-mcp-trust-policy.json + +aws iam put-role-policy \ + --role-name DefaultMCPRole \ + --policy-name DefaultMCPPolicy \ + --policy-document file://default-mcp-permissions.json +``` + +:::warning[Production: use permission boundaries] + +For production deployments, attach +[permission boundaries](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html) +to your IAM roles to limit the maximum permissions a role can grant. A good +boundary policy restricts allowed AWS regions, denies IAM and Organizations API +access, and denies `sts:AssumeRole` to prevent role chaining. This limits the +blast radius even if a role's identity policy is overly permissive. + +::: + +### Optional: additional roles for specific teams + +You can create additional roles with different permissions and map them to +specific groups using ToolHive's role mappings. This example creates a role that +grants `CallReadWriteTool` (so the `aws___call_aws` tool can execute API calls) +and scopes the underlying AWS permissions to S3 read-only access: + +```json title="s3-readonly-permissions.json" +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "aws-mcp:InvokeMcp", + "aws-mcp:CallReadOnlyTool", + "aws-mcp:CallReadWriteTool" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": ["s3:GetObject", "s3:ListBucket"], + "Resource": "*" + } + ] +} +``` + +The first statement unlocks the `aws___call_aws` tool. The second statement +limits what that tool can actually do - in this case, only S3 read operations. +Without the S3 permissions, API calls to other services would be denied by IAM. + +```bash +aws iam create-role \ + --role-name S3ReadOnlyMCPRole \ + --assume-role-policy-document file://default-mcp-trust-policy.json + +aws iam put-role-policy \ + --role-name S3ReadOnlyMCPRole \ + --policy-name S3ReadOnlyMCPPolicy \ + --policy-document file://s3-readonly-permissions.json +``` + +## Step 3: Create the MCPExternalAuthConfig + +Create an `MCPExternalAuthConfig` resource that defines how ToolHive exchanges +OIDC tokens for AWS credentials. + +```yaml {4,7} title="aws-sts-auth-config.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPExternalAuthConfig +metadata: + name: aws-mcp-sts-auth + namespace: toolhive-system +spec: + type: awsSts + awsSts: + region: + + # Default role when no role mapping matches + fallbackRoleArn: >- + arn:aws:iam:::role/DefaultMCPRole + + # Map JWT claims to IAM roles (lower priority = evaluated first) + roleMappings: + - claim: s3-readers + roleArn: >- + arn:aws:iam:::role/S3ReadOnlyMCPRole + priority: 10 +``` + +Replace the placeholders: + +- `` - the AWS region (for example, `us-east-1`) +- `` - your 12-digit AWS account ID + +:::info[How role selection works] + +When a request arrives, ToolHive evaluates your role mappings in priority order +(lower number = higher priority). The first matching rule determines which IAM +role to assume. If no mapping matches, the fallback role is used. + +For example, if a user belongs to both `s3-readers` and `developers` groups, and +`s3-readers` has a lower priority number, ToolHive selects the S3 read-only +role. + +::: + +Apply the configuration: + +```bash +kubectl apply -f aws-sts-auth-config.yaml +``` + +:::info[What's happening?] + +ToolHive checks the `groups` claim in the JWT by default (controlled by the +`roleClaim` field, which defaults to `"groups"` when omitted). Each mapping's +`claim` field is the **value** to match against that claim. In this example, if +the user's JWT contains `"s3-readers"` in their `groups` array, ToolHive assumes +the S3 read-only role. + +If your identity provider uses a different claim name (e.g., `roles` or +`memberOf`), add the `roleClaim` field: + +```yaml +awsSts: + roleClaim: roles # look at the "roles" claim instead of "groups" +``` + +For more complex matching logic, use CEL expressions in the `matcher` field +instead of `claim`: + +```yaml +roleMappings: + - matcher: '"admins" in claims["groups"] && claims["org"] == "engineering"' + roleArn: arn:aws:iam::123456789012:role/AdminMCPRole + priority: 1 +``` + +::: + +## Step 4: Deploy the MCPRemoteProxy + +Create an `MCPRemoteProxy` resource that points to the AWS MCP Server endpoint +and references the authentication configuration from the previous step. + +```yaml {7,10-11,14-19} title="aws-mcp-remote-proxy.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRemoteProxy +metadata: + name: aws-mcp-proxy + namespace: toolhive-system +spec: + remoteURL: https://aws-mcp.us-east-1.api.aws/mcp + + # Reference the AWS STS auth config from Step 3 + externalAuthConfigRef: + name: aws-mcp-sts-auth + + # OIDC configuration for validating incoming client tokens + oidcConfig: + type: inline + inline: + issuer: https:// + audience: + clientId: + + port: 8080 + transport: streamable-http + + audit: + enabled: true + + resources: + limits: + cpu: '500m' + memory: 512Mi + requests: + cpu: 100m + memory: 128Mi +``` + +Replace the placeholders with your OIDC provider's configuration. + +Apply the proxy: + +```bash +kubectl apply -f aws-mcp-remote-proxy.yaml +``` + +:::info[What's happening?] + +When you apply this resource, the ToolHive Operator: + +1. Creates a Deployment running the ToolHive proxy +2. Creates a Service to expose the proxy within the cluster +3. Configures the proxy to validate incoming OIDC tokens, exchange them for AWS + credentials via STS, and forward SigV4-signed requests to the AWS MCP Server + +::: + +## Step 5: Expose the proxy + +To make the proxy accessible to clients outside the cluster, create Gateway and +HTTPRoute resources. This example uses Kubernetes +[Gateway API](https://gateway-api.sigs.k8s.io/); if your cluster uses a +traditional Ingress controller, see +[Connect clients to MCP servers](../guides-k8s/connect-clients.mdx) for +alternatives. + +```yaml title="aws-mcp-gateway.yaml" +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: aws-mcp-gateway + namespace: toolhive-system +spec: + gatewayClassName: + listeners: + - name: https + protocol: HTTPS + port: 443 + hostname: + allowedRoutes: + namespaces: + from: All +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: aws-mcp-route + namespace: toolhive-system +spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: aws-mcp-gateway + namespace: toolhive-system + hostnames: + - + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: mcp-aws-mcp-proxy-remote-proxy + port: 8080 +``` + +Replace `` and `` with your gateway +configuration. + +```bash +kubectl apply -f aws-mcp-gateway.yaml +``` + +For more on exposing MCP servers, see +[Connect clients to MCP servers](../guides-k8s/connect-clients.mdx). For a +worked example using ngrok for development, see +[Configure secure ingress for MCP servers on Kubernetes](./ingress-ngrok.mdx). + +## Step 6: Verify the integration + +Check that all resources are running: + +```bash +# Verify the MCPExternalAuthConfig +kubectl get mcpexternalauthconfig -n toolhive-system + +# Verify the MCPRemoteProxy +kubectl get mcpremoteproxy -n toolhive-system + +# Check the proxy pods are running +kubectl get pods -n toolhive-system -l app.kubernetes.io/instance=aws-mcp-proxy +``` + +Test that unauthenticated requests are rejected: + +```bash +# Port-forward for local testing +kubectl port-forward -n toolhive-system \ + svc/mcp-aws-mcp-proxy-remote-proxy 8080:8080 & + +# This should return 401 Unauthorized +curl -s -o /dev/null -w "%{http_code}" \ + -X POST http://localhost:8080/mcp \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' +``` + +Now test with a valid OIDC token. This example uses +[oauth2c](https://github.com/cloudentity/oauth2c) and +[jq](https://jqlang.github.io/jq/) to obtain and extract a token, but any method +that produces a valid access token from your identity provider will work: + +```bash +TOKEN=$(oauth2c https:// \ + --client-id \ + --client-secret $OIDC_CLIENT_SECRET \ + --scopes openid \ + --grant-type authorization_code \ + --auth-method client_secret_basic \ + --response-mode form_post \ + --response-types code \ + --pkce | jq -r '.access_token') + +# This should return a list of tools +curl -X POST http://localhost:8080/mcp \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' +``` + +Check the proxy logs to confirm role selection is working: + +```bash +kubectl logs -n toolhive-system \ + -l app.kubernetes.io/instance=aws-mcp-proxy --tail=50 +``` + +Look for log entries showing role selection and STS exchange results. + +## Observability and audit + +ToolHive sets the STS session name to the user's `sub` claim from their JWT. +This means you can correlate ToolHive proxy logs with AWS CloudTrail entries for +the same user - look for CloudTrail events with +`eventName: AssumeRoleWithWebIdentity` and check +`requestParameters.roleSessionName` to identify who triggered each action. + +On the ToolHive side, the proxy logs role selection and STS exchange events +(user identity, matched claim, selected role, success or failure). You can also +enable Prometheus metrics and OpenTelemetry tracing on the MCPRemoteProxy - see +[Telemetry and metrics](../guides-k8s/telemetry-and-metrics.mdx) and the +[OpenTelemetry tutorial](./opentelemetry.mdx) for setup instructions. + +## Clean up + +Remove the Kubernetes resources: + +```bash +kubectl delete mcpremoteproxy aws-mcp-proxy -n toolhive-system +kubectl delete mcpexternalauthconfig aws-mcp-sts-auth -n toolhive-system +kubectl delete gateway aws-mcp-gateway -n toolhive-system +kubectl delete httproute aws-mcp-route -n toolhive-system +``` + +Remove the AWS IAM resources: + +```bash +aws iam delete-role-policy \ + --role-name DefaultMCPRole --policy-name DefaultMCPPolicy +aws iam delete-role --role-name DefaultMCPRole + +# If you created the optional S3 read-only role +aws iam delete-role-policy \ + --role-name S3ReadOnlyMCPRole --policy-name S3ReadOnlyMCPPolicy +aws iam delete-role --role-name S3ReadOnlyMCPRole + +aws iam delete-open-id-connect-provider \ + --open-id-connect-provider-arn \ + arn:aws:iam:::oidc-provider/ +``` + +## What's next? + +- Learn about the concepts behind + [backend authentication](../concepts/backend-auth.mdx) and + [token exchange](../guides-cli/token-exchange.mdx). +- Explore the + [authentication and authorization framework](../concepts/auth-framework.mdx) + for securing client-to-MCP-server connections. +- Read the AWS documentation on + [AssumeRoleWithWebIdentity](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html) + and + [IAM OIDC identity providers](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html). + +## Troubleshooting + +
+STS access denied: "Not authorized to perform +sts:AssumeRoleWithWebIdentity" + +This error means the IAM role's trust policy doesn't allow your OIDC provider to +assume it. Verify: + +- The OIDC provider ARN in the trust policy's `Federated` field matches the + provider you registered in Step 1. +- The `aud` condition matches the audience in your OIDC tokens. +- The OIDC provider's issuer URL in AWS IAM exactly matches the `iss` claim in + your tokens (including any path like `/oauth2/default`). + +Check the trust policy: + +```bash +aws iam get-role --role-name DefaultMCPRole \ + --query 'Role.AssumeRolePolicyDocument' +``` + +
+ +
+Service access denied: "User is not authorized to perform +aws-mcp:InvokeMcp" + +This error means the assumed role doesn't have the required permissions. Verify +the permission policy attached to the role includes the necessary actions: + +```bash +aws iam get-role-policy \ + --role-name DefaultMCPRole \ + --policy-name DefaultMCPPolicy +``` + +Ensure the policy includes `aws-mcp:InvokeMcp` and any other actions your MCP +tools require. + +
+ +
+Debugging token claims and role selection + +Obtain a token and inspect the output to verify your claims: + +```bash +oauth2c https:// \ + --client-id \ + --client-secret $OIDC_CLIENT_SECRET \ + --scopes openid \ + --grant-type authorization_code \ + --auth-method client_secret_basic \ + --response-mode form_post \ + --response-types code \ + --pkce +``` + +Check that the claim specified by `roleClaim` (default: `groups`) contains the +expected values. For example, if your role mapping uses `claim: s3-readers`, the +decoded token should include: + +```json +{ + "groups": ["s3-readers", "developers"] +} +``` + +If role mappings aren't matching as expected, check the proxy logs for role +selection details: + +```bash +kubectl logs -n toolhive-system \ + -l app.kubernetes.io/instance=aws-mcp-proxy | grep -i "role" +``` + +
+ +
+Proxy returns 401 Unauthorized + +If clients receive 401 errors, the OIDC token validation is failing. Verify: + +- The `issuer` in `oidcConfig` matches the `iss` claim in your token. +- The `audience` matches the `aud` claim. +- The token hasn't expired (check the `exp` claim). +- The proxy can reach your OIDC provider's JWKS endpoint from within the + cluster. + +Check proxy logs for authentication errors: + +```bash +kubectl logs -n toolhive-system \ + -l app.kubernetes.io/instance=aws-mcp-proxy | grep -i "auth" +``` + +
diff --git a/versioned_docs/version-1.0/toolhive/integrations/ingress-ngrok.mdx b/versioned_docs/version-1.0/toolhive/integrations/ingress-ngrok.mdx new file mode 100644 index 00000000..02aca2d1 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/integrations/ingress-ngrok.mdx @@ -0,0 +1,384 @@ +--- +title: Configure secure ingress for MCP servers on Kubernetes +description: + A guide to setting up secure ingress for MCP servers running on a Kubernetes + cluster using the ToolHive Operator. +--- + +In this tutorial, you'll configure secure ingress for MCP servers running on a +Kubernetes cluster using the ToolHive Operator. By the end, you'll have a +working MCP server accessible via a secure HTTPS endpoint that your team can +use. + +You'll use the [ngrok Kubernetes Operator](https://ngrok.com/docs/k8s) to create +secure tunnels to your MCP servers. This setup ensures that your MCP servers are +accessible over HTTPS, providing a secure way to interact with them. While this +tutorial uses ngrok for simplicity, you can apply similar principles with other +Kubernetes gateway solutions that work with the Kubernetes Gateway API, such as +[Traefik](https://doc.traefik.io/traefik/), +[Istio](https://istio.io/latest/docs/tasks/traffic-management/ingress/gateway-api/), +and +[many others](https://gateway-api.sigs.k8s.io/implementations/#implementations_1). + +:::tip + +This tutorial demonstrates ngrok specifically. For general guidance on exposing +MCP servers with Ingress, Gateway API, or cloud provider implementations, see +[Connect clients to MCP servers](../guides-k8s/connect-clients.mdx). + +::: + +## What you'll learn + +This tutorial demonstrates how to make MCP servers available centrally for teams +and enterprises. By deploying MCP servers on Kubernetes with secure ingress, you +create a shared pool of capabilities that any team member can access. This +approach is valuable for organizations that want to provide standardized tools +and resources to developers while maintaining control over security, access +policies, and resource usage. + +Once your MCP servers are accessible via HTTPS, users can quickly connect their +AI clients using the ToolHive CLI (`thv run`) or UI without needing to install +or configure individual MCP servers locally. This centralized model simplifies +deployment, improves consistency, and makes it easier to manage updates and +security patches. + +In this tutorial, you'll learn: + +- How to install and configure the ngrok Kubernetes Operator. +- How to create secure tunnels for MCP servers. +- How to access MCP servers via HTTPS. + +## Prerequisites + +Before you begin, ensure you have the following: + +- A Kubernetes cluster with the ToolHive Operator installed. See the + [Kubernetes quickstart guide](../guides-k8s/quickstart.mdx) for instructions. +- `kubectl` command-line tool configured to interact with your cluster. +- An [ngrok account](https://ngrok.com/) to obtain an authentication token. A + free account is sufficient for this tutorial. +- The [ToolHive CLI](../guides-cli/install.mdx) to interact with the remote MCP + server and connect it to your AI clients. + +## Step 1: Install the ngrok Kubernetes Operator + +These steps are a simplified version of the instructions found in the +[ngrok documentation](https://ngrok.com/docs/getting-started/kubernetes/gateway-api). + +First, you'll need to install the ngrok Kubernetes Operator in your cluster +using Helm: + +```bash +helm repo add ngrok https://charts.ngrok.com +helm repo update +``` + +Obtain your authentication token and create an API key from the ngrok dashboard, +then set them as environment variables: + +```bash +export NGROK_AUTHTOKEN="your_ngrok_auth_token" +export NGROK_API_KEY="your_ngrok_api_key" +``` + +Install the Kubernetes Gateway API CRDs and a GatewayClass resource: + +```bash +kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/standard-install.yaml + +kubectl apply -f -<-proxy`. For the MKP example, the +service is `mcp-mkp-proxy` in the `toolhive-system` namespace on port `8080`: + +```bash +kubectl get service mcp-mkp-proxy -n toolhive-system +``` + +The output should look similar to this: + +```plaintext +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +mcp-mkp-proxy ClusterIP 10.96.106.88 8080/TCP 2m19s +``` + +Note the service name and port number for the next step. + +## Step 3: Create Gateway and HTTPRoute resources + +Now, create a Gateway and HTTPRoute resource to expose the MCP server securely +via ngrok. + +For this step, you'll need to obtain your dev domain or custom domain from the +[ngrok dashboard](https://dashboard.ngrok.com/domains). If you have a free +account, it will be in the format `.ngrok-free.app`. Replace +`` with your actual domain in the YAML below. + +Create a file named `ngrok-mcp-gateway.yaml` with the following content: + +```yaml {12} title="ngrok-mcp-gateway.yaml" +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: ngrok-gateway + namespace: toolhive-system +spec: + gatewayClassName: ngrok + listeners: + - name: https + protocol: HTTPS + port: 443 + hostname: + allowedRoutes: + namespaces: + from: All +``` + +Then create a file named `ngrok-mcp-httproute.yaml` with the following content: + +```yaml {13,20-22} title="ngrok-mcp-httproute.yaml" +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: mkp-mcp-route + namespace: toolhive-system +spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: ngrok-gateway + namespace: toolhive-system + hostnames: + - + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: mcp-mkp-proxy # Replace with your service name from Step 2 + port: 8080 # Replace with your port number from Step 2 +``` + +Apply the configurations to your cluster: + +```bash +kubectl apply -f ngrok-mcp-gateway.yaml +kubectl apply -f ngrok-mcp-httproute.yaml +``` + +## Step 4: Access the MCP server + +After a few moments, the ngrok Operator will create a secure tunnel to your MCP +server. You can access it using the domain you specified in the Gateway +resource. + +Use the ToolHive CLI to verify connectivity to the MCP server: + +```bash +thv mcp list tools --server https:///mcp +``` + +The `/mcp` path is the default endpoint for the MCP Streamable HTTP transport. +The output should display a list of tools managed by the MCP server, confirming +that you have successfully set up secure ingress using ngrok. For the "MKP" MCP +server, you should see output similar to this: + +```plaintext +TOOLS: +NAME DESCRIPTION +get_resource Get a Kubernetes resource or its subresource +list_resources List Kubernetes resources +``` + +Use the ToolHive CLI or UI to connect your AI clients to the MCP server: + +```bash +thv run --name mkp https:///mcp +``` + +The MKP MCP server is now available to AI clients configured with +`thv client setup` via a secure HTTPS endpoint. + +## Optional: Tunnel multiple MCP servers with URL rewrites + +The previous steps exposed a single MCP server at the root path (`/`). If you +have multiple MCP servers and want to expose them via the same ngrok Gateway, +you can use path-based routing with URL rewrites in the HTTPRoute resource. This +allows you to route requests to different MCP servers based on path prefixes +like `/mkp` and `/fetch`. + +:::note + +This option consumes $1 of the $5 credit included with the free ngrok account to +enable the traffic policies feature or requires a paid ngrok plan. + +::: + +Run a second MCP server, for example: + +```bash +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/heads/main/examples/operator/mcp-servers/mcpserver_fetch.yaml +``` + +Then, update the `ngrok-mcp-httproute.yaml` file. Update the `rules` section of +the existing HTTPRoute resource to give the MKP MCP server a path prefix of +`/mkp` and add a `URLRewrite` filter. + +```yaml {18,22-27} showLineNumbers title="ngrok-mcp-httproute.yaml" +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: mcp-servers-route + namespace: toolhive-system +spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: ngrok-gateway + namespace: toolhive-system + hostnames: + - + rules: + - matches: + - path: + type: PathPrefix + value: /mkp + backendRefs: + - name: mcp-mkp-proxy + port: 8080 + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplacePrefixMatch + replacePrefixMatch: '' # Normally this would be '/' but ngrok requires empty string +``` + +Add another rule for the Fetch MCP server with a path prefix of `/fetch`: + +{/* prettier-ignore */} +```yaml showLineNumbers=28 title="ngrok-mcp-httproute.yaml" + - matches: + - path: + type: PathPrefix + value: /fetch + backendRefs: + - name: mcp-fetch-proxy + port: 8080 + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplacePrefixMatch + replacePrefixMatch: '' +``` + +Apply the updated configuration to your cluster: + +```bash +kubectl apply -f ngrok-mcp-httproute.yaml +``` + +You can now access both MCP servers using the same ngrok domain with different +path prefixes: + +```bash +thv mcp list tools --server https:///mkp/mcp +thv mcp list tools --server https:///fetch/mcp +``` + +Use the ToolHive CLI or UI to connect your AI clients to either MCP server: + +```bash +thv run --name mkp https:///mkp/mcp +thv run --name fetch https:///fetch/mcp +``` + +## Clean up + +To remove the ngrok resources from your cluster and ngrok account, run the +following: + +```bash +# Delete the HTTPRoute and Gateway resources +kubectl delete -f ngrok-mcp-httproute.yaml +kubectl delete -f ngrok-mcp-gateway.yaml + +# Delete the ngrok CRDs +kubectl delete $(kubectl get crd -o name | grep "ngrok") + +# Uninstall the ngrok Operator +helm uninstall ngrok-operator -n ngrok-operator +kubectl delete namespace ngrok-operator +``` + +## What's next? + +Now that you have secure ingress configured for your MCP servers, consider these +next steps: + +- Explore [authentication and authorization](../concepts/auth-framework.mdx) to + control access to your MCP servers. +- Learn about [observability](../concepts/observability.mdx) to monitor your MCP + server usage and performance. +- Try other gateway solutions like Traefik or Istio if they're already part of + your infrastructure. + +## Addendum: Combining with MCP server authentication + +When exposing MCP servers via ngrok or any other ingress solution, consider the +security implications. While ngrok provides secure HTTPS tunnels, you should +also implement authentication and authorization to control access. The ToolHive +Operator supports +[OAuth-based authentication methods](../guides-k8s/auth-k8s.mdx) that are out of +scope for this tutorial but essential for production deployments. + +When OAuth is enabled on an MCP server, add additional rules to the HTTPRoute +resource to expose the OAuth metadata endpoint for proper authentication flow +through the gateway. + +Here's an example rule to add to the `mcp-servers-route` HTTPRoute in your +`ngrok-mcp-httproute.yaml` file. Add this rule alongside the existing `/mkp` +path rule: + +{/* prettier-ignore */} +```yaml title="ngrok-mcp-httproute.yaml" + - matches: + - path: + type: Exact + value: /.well-known/oauth-protected-resource/mkp/mcp + backendRefs: + - name: mcp-mkp-proxy + port: 8080 +``` diff --git a/versioned_docs/version-1.0/toolhive/integrations/okta.mdx b/versioned_docs/version-1.0/toolhive/integrations/okta.mdx new file mode 100644 index 00000000..ab5680de --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/integrations/okta.mdx @@ -0,0 +1,492 @@ +--- +title: Role-based authorization with Okta +description: + Deploy a GitHub MCP server on Kubernetes, add Okta OIDC authentication, and + enforce role-based access control (RBAC) with Cedar policies. +--- + +Without authorization, every authenticated user has access to every tool an MCP +server exposes. By the end of this tutorial, you'll have a GitHub MCP server on +Kubernetes secured with Okta OpenID Connect (OIDC) authentication and Cedar +role-based access control (RBAC) policies — writers get full access while +readers see only read-only tools. + +:::tip[Using a different identity provider?] + +This tutorial uses Okta, but the pattern applies to any OIDC provider. Only Step +1 is Okta-specific — it covers creating an OIDC application, setting up groups, +and adding a groups claim to the token. If you use a different provider +(Keycloak, Entra ID, Auth0, etc.), complete the equivalent setup in your +provider, then pick up from [Step 2](#step-2-deploy-the-github-mcp-server) +onward. The Kubernetes manifests, Cedar policies, and `oidcConfig` fields all +consume standard OIDC values regardless of which provider issued them. + +::: + +## Prerequisites + +:::info[Prerequisites] + +Before starting this tutorial, make sure you have: + +- A Kubernetes cluster with the ToolHive Operator installed. See the + [Kubernetes quickstart guide](../guides-k8s/quickstart.mdx) for setup + instructions. +- `kubectl` configured to access your cluster +- A + [GitHub personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) + (PAT) with `repo` scope +- An Okta developer account (free at + [developer.okta.com](https://developer.okta.com/)) +- Basic familiarity with OAuth, OIDC, and JSON Web Token (JWT) concepts. For + background, see + [Authentication and authorization framework](../concepts/auth-framework.mdx). + +::: + +## Step 1: Configure Okta + +Set up your Okta environment with an OIDC application, user groups, and a groups +claim so that ToolHive can authenticate users and read their group memberships +from JWTs. + +### Create an OIDC application + +1. Sign in to the Okta admin console. +2. Go to **Applications** > **Applications** > **Create App Integration**. +3. Select **OIDC - OpenID Connect** and **Web Application**, then click + **Next**. +4. Set the sign-in redirect URI to `http://localhost:8080/callback` (for local + testing). +5. Under **Assignments**, assign the app to the groups you create in the next + section. +6. Click **Save**. + +### Create groups and users + +1. Go to **Directory** > **Groups**. +2. Create two groups: `mcp-read` and `mcp-write`. +3. Create (or assign) two test users. Add Alice to `mcp-read` only. Add Bob to + both `mcp-read` and `mcp-write`. + +### Add a groups claim to your identity provider + +1. Go to **Security** > **API** > **Authorization Servers**. +2. Select the `default` authorization server (or your custom one). +3. Go to **Claims** > **Add Claim**. +4. Set the following values: + - **Name**: `groups` + - **Include in**: **ID Token** and **Access Token** + - **Value type**: **Groups** + - **Filter**: **Matches regex** `.*` +5. Click **Create**. +6. Note the **Issuer URI** from the authorization server settings (for example, + `https://YOUR_OKTA_DOMAIN/oauth2/default`). + +### Collect your configuration values + +After setup, you need three values from Okta. Use the table below to locate each +one: + +| Value | Where to find it | Example | +| ---------- | ------------------------------------------------------------------------------------------- | --------------------------------------------------- | +| Issuer URL | **Security** > **API** > **Authorization Servers** > Issuer URI | `https://dev-12345.okta.com/oauth2/default` | +| Audience | `api://default` for the default authorization server, or the custom audience you configured | `api://default` | +| JWKS URL | Issuer URL + `/v1/keys` | `https://dev-12345.okta.com/oauth2/default/v1/keys` | + +## Step 2: Deploy the GitHub MCP server + +### Create a Secret for your GitHub PAT + +Store your GitHub personal access token as a Kubernetes Secret. + +:::tip + +Your PAT needs the `repo` scope for write tools (like `create_pull_request` and +`push_files`) to appear. Without it, the GitHub MCP server only exposes +read-only tools regardless of your Cedar policies. + +::: + +```yaml title="github-pat-secret.yaml" +apiVersion: v1 +kind: Secret +metadata: + name: github-pat + namespace: toolhive-system +type: Opaque +stringData: + token: 'YOUR_GITHUB_PAT' +``` + +```bash +kubectl apply -f github-pat-secret.yaml +``` + +### Deploy the MCPServer without authentication + +Create the MCPServer resource without authentication to verify the basic +deployment works: + +```yaml title="github-mcpserver.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: github + namespace: toolhive-system +spec: + image: ghcr.io/github/github-mcp-server:latest + transport: stdio + secrets: + - name: github-pat + key: token + targetEnvName: GITHUB_PERSONAL_ACCESS_TOKEN + resources: + limits: + cpu: '200m' + memory: '256Mi' + requests: + cpu: '100m' + memory: '128Mi' +``` + +```bash +kubectl apply -f github-mcpserver.yaml +``` + +### Verify the server is running + +Check the status of the MCPServer resource: + +```bash +kubectl get mcpserver -n toolhive-system github +``` + +You should see output similar to: + +```text +NAME STATUS URL AGE +github Running http://mcp-github-proxy.toolhive-system.svc.cluster.local:8080 30s +``` + +Wait until the status shows `Running` before continuing to the next step. If the +server remains in a pending state, check the operator logs for errors: + +```bash +kubectl logs -n toolhive-system deployment/toolhive-operator +``` + +## Step 3: Add Okta OIDC authentication + +Update the MCPServer to include an `oidcConfig` section. Replace the placeholder +values with the configuration values you collected in +[Step 1](#collect-your-configuration-values): + +```yaml title="github-mcpserver-oidc.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: github + namespace: toolhive-system +spec: + image: ghcr.io/github/github-mcp-server:latest + transport: stdio + secrets: + - name: github-pat + key: token + targetEnvName: GITHUB_PERSONAL_ACCESS_TOKEN + # highlight-start + oidcConfig: + type: inline + inline: + issuer: 'YOUR_ISSUER_URL' + audience: 'YOUR_AUDIENCE' + jwksUrl: 'YOUR_JWKS_URL' + # highlight-end + # ... resources same as before +``` + +```bash +kubectl apply -f github-mcpserver-oidc.yaml +``` + +After applying this change, the MCP server requires a valid JWT on every +request. Unauthenticated requests now return `401 Unauthorized`. + +:::tip + +To test authenticated requests, you can use a tool like +[oauth2c](https://github.com/cloudentity/oauth2c) to obtain tokens from Okta, or +use your Okta admin console to generate a test token from **Security** > +**API** > **Authorization Servers** > **Token Preview**. + +::: + +## Step 4: Add Cedar policies for role-based access + +Now that authentication is in place, add authorization policies. In this step, +you define Cedar policies that give writers full access while restricting +readers to a set of read-only tools. + +### Define the roles + +The following table summarizes the two roles and the tools each role can access: + +| Role | Group | Allowed tools | +| ------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| Writer | `mcp-write` | All tools (read and write) | +| Reader | `mcp-read` | Read-only tools: `get_file_contents`, `list_commits`, `list_branches`, `list_issues`, `list_pull_requests`, `search_issues`, `get_me` | + +### Create the authorization ConfigMap + +:::note + +ToolHive exposes JWT claims to Cedar policies with a `claim_` prefix, so the +`groups` claim you configured in Okta becomes `principal.claim_groups` in policy +expressions. For more details, see +[Working with JWT claims](../concepts/cedar-policies.mdx#working-with-jwt-claims). + +::: + +Create a ConfigMap containing the Cedar policies: + +```yaml title="authz-config.yaml" +apiVersion: v1 +kind: ConfigMap +metadata: + name: github-authz + namespace: toolhive-system +data: + authz-config.json: | + { + "version": "1.0", + "type": "cedarv1", + "cedar": { + "policies": [ + "permit(principal, action, resource) when { principal.claim_groups.contains(\"mcp-write\") };", + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"get_file_contents\") when { principal.claim_groups.contains(\"mcp-read\") };", + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"list_commits\") when { principal.claim_groups.contains(\"mcp-read\") };", + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"list_branches\") when { principal.claim_groups.contains(\"mcp-read\") };", + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"list_issues\") when { principal.claim_groups.contains(\"mcp-read\") };", + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"list_pull_requests\") when { principal.claim_groups.contains(\"mcp-read\") };", + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"search_issues\") when { principal.claim_groups.contains(\"mcp-read\") };", + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"get_me\") when { principal.claim_groups.contains(\"mcp-read\") };" + ], + "entities_json": "[]" + } + } +``` + +```bash +kubectl apply -f authz-config.yaml +``` + +### Update the MCPServer with authorization + +Add the `authzConfig` section to reference the ConfigMap you just created. The +highlighted lines show the new addition: + +```yaml title="github-mcpserver-authz.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: github + namespace: toolhive-system +spec: + image: ghcr.io/github/github-mcp-server:latest + transport: stdio + secrets: + - name: github-pat + key: token + targetEnvName: GITHUB_PERSONAL_ACCESS_TOKEN + oidcConfig: + type: inline + inline: + issuer: 'YOUR_ISSUER_URL' + audience: 'YOUR_AUDIENCE' + jwksUrl: 'YOUR_JWKS_URL' + # highlight-start + authzConfig: + type: configMap + configMap: + name: github-authz + key: authz-config.json + # highlight-end + # ... resources same as before +``` + +```bash +kubectl apply -f github-mcpserver-authz.yaml +``` + +## Step 5: Verify tool filtering + +ToolHive automatically filters the `tools/list` response based on your Cedar +policies. When a client calls `tools/list`, the proxy evaluates each tool +against the user's policies and only returns the tools the user is allowed to +call. For more details, see +[list operations and filtering](../concepts/cedar-policies.mdx#list-operations-and-filtering). + +**Writer** (Bob, in both `mcp-write` and `mcp-read`) sees all available tools: + +```text +add_issue_comment, create_branch, create_pull_request, +create_repository, get_file_contents, list_branches, +list_commits, list_issues, merge_pull_request, ... (truncated) +``` + +**Reader** (Alice, in `mcp-read` only) sees only the read-only tools permitted +by the Cedar policies: + +```text +get_file_contents, get_me, list_branches, +list_commits, list_issues, list_pull_requests, +search_issues +``` + +:::note + +Tool filtering happens automatically. You don't need separate policies for +`list_tools`. The proxy evaluates `call_tool` policies for each tool and only +returns tools the user is allowed to call. + +::: + +## Step 6: Verify denied access + +Verify that authorization is enforced by calling a write tool with a reader's +token. + +First, port-forward to the MCP server service so you can send requests from your +local machine: + +```bash +kubectl port-forward -n toolhive-system svc/mcp-github-proxy 8080:8080 +``` + +:::note + +Port-forwarding works well for testing. In production, expose your MCP servers +using an Ingress or Gateway API resource instead. See +[Connect clients to MCP servers](../guides-k8s/connect-clients.mdx) for +configuration options. + +::: + +In a separate terminal, send a request using a reader's token: + +```bash +# Using a reader's token to call a write operation +curl -X POST http://localhost:8080/mcp \ + -H "Authorization: Bearer READER_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "jsonrpc": "2.0", + "method": "tools/call", + "params": { + "name": "create_pull_request", + "arguments": { + "repo": "example/repo", + "title": "Test", + "head": "feature", + "base": "main" + } + }, + "id": 1 + }' +``` + +The proxy denies the request and returns a `403 Forbidden` response. Sending the +same request with a writer's token succeeds — the proxy forwards the request to +the MCP server and returns the tool's response. + +## Clean up + +Remove the resources you created in this tutorial: + +```bash +kubectl delete mcpserver -n toolhive-system github +kubectl delete configmap -n toolhive-system github-authz +kubectl delete secret -n toolhive-system github-pat +``` + +## What's next? + +- Learn more about Cedar policy syntax in + [Cedar policies](../concepts/cedar-policies.mdx) +- Explore the + [authentication and authorization framework](../concepts/auth-framework.mdx) + concepts +- Set up [token exchange](../guides-k8s/token-exchange-k8s.mdx) for downstream + service authentication +- Deploy a [Virtual MCP Server](../guides-vmcp/index.mdx) to aggregate multiple + servers behind a single endpoint + +## Troubleshooting + +
+Authentication issues + +If clients can't authenticate: + +1. Check that the JWT is valid and not expired. +2. Verify that the audience and issuer match your `oidcConfig` values. +3. Ensure the JWKS URL is accessible from within the cluster. +4. Check the operator and proxy logs for specific errors: + + ```bash + # Operator logs + kubectl logs -n toolhive-system deployment/toolhive-operator + + # Proxy logs for the GitHub MCPServer + kubectl logs -n toolhive-system \ + -l app.kubernetes.io/managed-by=toolhive,app.kubernetes.io/name=github \ + -c proxy + ``` + +
+ +
+Authorization issues + +If authenticated clients are denied access: + +1. Make sure your Cedar policies explicitly permit the specific action + (remember, default deny). +2. Check that the principal, action, and resource match what's in your policies, + including capitalization and formatting. +3. Examine any conditions in your policies to ensure they're satisfied (for + example, required JWT claims). + +
+ +
+Token missing groups claim + +Verify that the `groups` claim is configured on the **authorization server**, +not just on the application. In the Okta admin console, go to **Security** > +**API** > **Authorization Servers**, select your server, and check the +**Claims** tab. + +
+ +
+Groups not matching Cedar policies + +Group names in Cedar policies must exactly match the Okta group names, including +capitalization. For example, `mcp-write` is not the same as `MCP-Write`. Check +your Okta group names under **Directory** > **Groups** and update your Cedar +policies if needed. + +
+ +
+401 after adding oidcConfig + +Verify that the issuer URL includes the full authorization server path. For the +default Okta authorization server, the issuer URL should end with +`/oauth2/default` (for example, `https://dev-12345.okta.com/oauth2/default`). A +common mistake is to use just the Okta domain without the authorization server +path. + +
diff --git a/versioned_docs/version-1.0/toolhive/integrations/opentelemetry.mdx b/versioned_docs/version-1.0/toolhive/integrations/opentelemetry.mdx new file mode 100644 index 00000000..2dec4dc6 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/integrations/opentelemetry.mdx @@ -0,0 +1,996 @@ +--- +title: Collect telemetry for MCP workloads +description: + Learn how to collect metrics and traces for MCP workloads using either the + ToolHive CLI or Kubernetes operator with OpenTelemetry, Jaeger, and + Prometheus. +toc_max_heading_level: 2 +--- + +import useBaseUrl from '@docusaurus/useBaseUrl'; +import ThemedImage from '@theme/ThemedImage'; + +In this tutorial, you'll set up comprehensive observability for your MCP +workloads using [OpenTelemetry](https://opentelemetry.io/) with +[Jaeger](https://www.jaegertracing.io/) for distributed tracing, +[Prometheus](https://prometheus.io/) for metrics collection, and +[Grafana](https://grafana.com/) for visualization. + +By the end, you'll have a complete, industry-standard observability solution +that captures detailed traces and metrics, giving you visibility into your MCP +server performance and usage patterns. + + + +## Choose your deployment path + +This tutorial offers two paths for MCP observability: + + + + +**ToolHive CLI + Docker observability stack** + +Use the ToolHive CLI to run MCP servers locally, with Jaeger and Prometheus +running in Docker containers. This approach is perfect for: + +- Local development and testing +- Quick setup and experimentation +- Individual developer workflows +- Learning OpenTelemetry concepts + + + + +**ToolHive Kubernetes Operator + in-cluster observability** + +Use the ToolHive Kubernetes operator to manage MCP servers in a cluster, with +Jaeger and Prometheus deployed inside Kubernetes. This approach is ideal for: + +- Production-like environments +- Team collaboration and shared infrastructure +- Container orchestration workflows +- Scalable observability deployments + + + + +:::tip[Choose one path] + +Select your preferred deployment method using the tabs above. All subsequent +steps will show instructions for your chosen path. + +::: + +## What you'll learn + +- How to deploy Jaeger and Prometheus for your chosen environment +- How to configure OpenTelemetry collection for ToolHive MCP servers +- How to analyze traces in Jaeger and metrics in Prometheus +- How to set up queries and monitoring for MCP workloads +- Best practices for observability in your deployment environment + +## Prerequisites + +Before starting this tutorial, make sure you have: + + + + +- Completed the [ToolHive CLI quickstart](../guides-cli/quickstart.mdx) +- A supported container runtime installed and running. + [Docker](https://docs.docker.com/get-docker/) or + [Podman](https://podman-desktop.io/downloads) are recommended for this + tutorial +- [Docker Compose](https://docs.docker.com/compose/install/) or + [Podman Compose](https://podman-desktop.io/docs/compose/setting-up-compose) + available +- A [supported MCP client](../reference/client-compatibility.mdx) for testing + + + + +- Completed the [ToolHive Kubernetes quickstart](../guides-k8s/quickstart.mdx) + with a local kind cluster +- [`kubectl`](https://kubernetes.io/docs/tasks/tools/) configured to access your + cluster +- [Helm](https://helm.sh/docs/intro/install/) (v3.10 minimum) installed +- A [supported MCP client](../reference/client-compatibility.mdx) for testing +- The [ToolHive CLI](../guides-cli/quickstart.mdx) (optional, for client + configuration) +- Basic familiarity with Kubernetes concepts + + + + +## Overview + +The architecture for each deployment method: + + + + +```mermaid +graph TB + A[AI client] + THV[ToolHive CLI] + Proxy[Proxy process] + subgraph Docker[**Docker**] + MCP[MCP server
container] + OTEL[OTel Collector] + J[Jaeger] + P[Prometheus] + G[Grafana] + end + + THV -. manages .-> MCP & Proxy + Proxy -- HTTP or stdio --> MCP + Proxy -- OpenTelemetry data --> OTEL + OTEL -- traces --> J + OTEL -- metrics --> P + G -- visualization --> P + A -- HTTP --> Proxy +``` + +Your setup will include: + +- **ToolHive CLI** managing MCP servers in containers +- **Jaeger** for distributed tracing with built-in UI +- **Prometheus** for metrics collection with web UI +- **OpenTelemetry Collector** forwarding data to both backends + +
+ + +```mermaid +graph TB + A[AI client] + subgraph K8s[**K8s Cluster**] + THV[ToolHive Operator] + Proxy[Proxyrunner pod] + MCP[MCP server pod] + OTEL[OTel Collector] + J[Jaeger] + P[Prometheus] + G[Grafana] + end + + A -- HTTP
via ingress --> Proxy + THV -. manages .-> Proxy + Proxy -. manages .-> MCP + Proxy -- OpenTelemetry data --> OTEL + OTEL -- traces --> J + OTEL -- metrics --> P + G -- visualization --> P +``` + +Your setup will include: + +- **ToolHive Operator** managing MCP servers as Kubernetes pods +- **Jaeger** for distributed tracing +- **Prometheus** for metrics collection +- **Grafana** for metrics visualization +- **OpenTelemetry Collector** running as a Kubernetes service + +
+
+ +## Step 1: Deploy the observability stack + +First, set up the observability infrastructure for your chosen environment. + + + + +### Create Docker Compose configuration + +Create a Docker Compose file for the observability stack: + +```yaml title="observability-stack.yml" +services: + jaeger: + image: jaegertracing/jaeger:latest + container_name: jaeger + environment: + - COLLECTOR_OTLP_ENABLED=true + ports: + - '16686:16686' # Jaeger UI + networks: + - observability + + prometheus: + image: prom/prometheus:latest + container_name: prometheus + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + - '--web.console.libraries=/etc/prometheus/console_libraries' + - '--web.console.templates=/etc/prometheus/consoles' + - '--web.enable-lifecycle' + - '--enable-feature=native-histograms' + ports: + - '9090:9090' + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml + - prometheus-data:/prometheus + networks: + - observability + + grafana: + image: grafana/grafana:latest + container_name: grafana + environment: + - GF_SECURITY_ADMIN_USER=admin + - GF_SECURITY_ADMIN_PASSWORD=admin + - GF_USERS_ALLOW_SIGN_UP=false + ports: + - '3000:3000' + volumes: + - ./grafana-prometheus.yml:/etc/grafana/provisioning/datasources/prometheus.yml + - grafana-data:/var/lib/grafana + networks: + - observability + + otel-collector: + image: otel/opentelemetry-collector-contrib:latest + container_name: otel-collector + command: ['--config=/etc/otel-collector-config.yml'] + volumes: + - ./otel-collector-config.yml:/etc/otel-collector-config.yml + ports: + - '4318:4318' # OTLP HTTP receiver (ToolHive sends here) + - '8889:8889' # Prometheus exporter metrics + depends_on: + - jaeger + - prometheus + networks: + - observability + +volumes: + prometheus-data: + grafana-data: + +networks: + observability: + driver: bridge +``` + +### Configure the OpenTelemetry Collector + +Create the collector configuration to export to both Jaeger and Prometheus: + +```yaml title="otel-collector-config.yml" +receivers: + otlp: + protocols: + http: + endpoint: 0.0.0.0:4318 + +processors: + batch: + timeout: 10s + send_batch_size: 1024 + +exporters: + # Export traces to Jaeger + otlp/jaeger: + endpoint: jaeger:4317 + tls: + insecure: true + + # Expose metrics for Prometheus + prometheus: + endpoint: 0.0.0.0:8889 + const_labels: + service: 'toolhive-mcp-proxy' + +service: + pipelines: + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp/jaeger] + metrics: + receivers: [otlp] + processors: [batch] + exporters: [prometheus] +``` + +### Configure Prometheus and Grafana + +Create a Prometheus configuration to scrape the OpenTelemetry Collector: + +```yaml title="prometheus.yml" +global: + scrape_interval: 15s + +scrape_configs: + - job_name: 'otel-collector' + static_configs: + - targets: ['otel-collector:8889'] +``` + +Create the Prometheus data source configuration for Grafana: + +```yaml title="grafana-prometheus.yml" +apiVersion: 1 + +datasources: + - name: prometheus + type: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: true +``` + +### Start the observability stack + +Deploy the stack and verify it's running: + +```bash +# Start the stack +docker compose -f observability-stack.yml up -d + +# Verify Jaeger is running +curl http://localhost:16686/api/services + +# Verify Prometheus is running +curl http://localhost:9090/-/healthy + +# Verify the OpenTelemetry Collector is ready +curl -I http://localhost:8889/metrics +``` + +Access the interfaces: + +- **Jaeger UI**: `http://localhost:16686` +- **Prometheus Web UI**: `http://localhost:9090` +- **Grafana**: `http://localhost:3000` (login: admin/admin) + + + + +### Prerequisite + +If you've completed the [Kubernetes quickstart](../guides-k8s/quickstart.mdx), +skip to the next step. + +Otherwise, set up a local kind cluster and install the ToolHive operator: + +```bash +kind create cluster --name toolhive +helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds +helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator -n toolhive-system --create-namespace +``` + +Verify the operator is running: + +```bash +kubectl get pods -n toolhive-system +``` + +### Create the monitoring namespace + +Create a dedicated namespace for your observability stack: + +```bash +kubectl create namespace monitoring +``` + +### Deploy Jaeger + +Install Jaeger using Helm with a configuration suited for ToolHive: + +```bash +helm repo add jaegertracing https://jaegertracing.github.io/helm-charts +helm repo update +helm upgrade -i jaeger-all-in-one jaegertracing/jaeger -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.3.6/examples/otel/jaeger-values.yaml -n monitoring +``` + +### Deploy Prometheus and Grafana + +Install Prometheus and Grafana using the kube-prometheus-stack Helm chart: + +```bash +helm repo add prometheus-community https://prometheus-community.github.io/helm-charts +helm repo update +helm upgrade -i kube-prometheus-stack prometheus-community/kube-prometheus-stack -f https://raw.githubusercontent.com/stacklok/toolhive/v0.3.6/examples/otel/prometheus-stack-values.yaml -n monitoring +``` + +### Deploy OpenTelemetry Collector + +Create the collector configuration and deployment manifest: + +```bash +helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts +helm repo update +helm upgrade -i otel-collector open-telemetry/opentelemetry-collector -f https://raw.githubusercontent.com/stacklok/toolhive/v0.3.6/examples/otel/otel-values.yaml -n monitoring +``` + +### Verify all components + +Verify all components are running: + +```bash +kubectl get pods -n monitoring +``` + +Wait for all pods to be in Running status before proceeding. The output should +look similar to: + +```text +NAME READY STATUS RESTARTS AGE +jaeger-all-in-one-6bf667c984-p5455 1/1 Running 0 2m12s +kube-prometheus-stack-grafana-69c88f77c5-b9f7m 3/3 Running 0 37s +kube-prometheus-stack-kube-state-metrics-55cb9c8889-cnlkt 1/1 Running 0 37s +kube-prometheus-stack-operator-85655fb7cd-rxms9 1/1 Running 0 37s +kube-prometheus-stack-prometheus-node-exporter-zzcvh 1/1 Running 0 37s +otel-collector-opentelemetry-collector-agent-hqtnq 1/1 Running 0 11s +prometheus-kube-prometheus-stack-prometheus-0 2/2 Running 0 36s +``` + + + + +## Step 2: Configure MCP server telemetry + +Now configure your MCP servers to send telemetry data to the observability +stack. + + + + +### Set global telemetry configuration + +Configure ToolHive CLI with default telemetry settings to send data to the +OpenTelemetry Collector: + +```bash +# Configure the OpenTelemetry endpoint (collector, not directly to Jaeger) +thv config otel set-endpoint localhost:4318 + +# Enable both metrics and tracing +thv config otel set-metrics-enabled true +thv config otel set-tracing-enabled true + +# Set 100% sampling for development +thv config otel set-sampling-rate 1.0 + +# Use insecure connection for local development +thv config otel set-insecure true +``` + +### Run an MCP server with telemetry + +Start an MCP server with enhanced telemetry configuration: + +```bash +thv run \ + --otel-service-name "mcp-fetch-server" \ + --otel-env-vars "USER,HOST" \ + --otel-enable-prometheus-metrics-path \ + fetch +``` + +Verify the server started and is exporting telemetry: + +```bash +# Check server status +thv list + +# Check Prometheus metrics are available on the MCP server +PORT=$(thv list | grep fetch | awk '{print $5}') +curl http://localhost:$PORT/metrics +``` + + + + +### Create an MCP server with telemetry + +Create an MCPServer resource with comprehensive telemetry configuration: + +```yaml {18-30} title="fetch-with-telemetry.yml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: fetch-telemetry + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/gofetch/server + transport: streamable-http + proxyPort: 8080 + mcpPort: 8080 + resources: + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' + telemetry: + openTelemetry: + enabled: true + endpoint: otel-collector-opentelemetry-collector.monitoring.svc.cluster.local:4318 + serviceName: mcp-fetch-server + insecure: true # Using HTTP collector endpoint + metrics: + enabled: true + tracing: + enabled: true + samplingRate: '1.0' + prometheus: + enabled: true +``` + +Deploy the MCP server: + +```bash +kubectl apply -f fetch-with-telemetry.yml +``` + +Verify the MCP server is running and healthy: + +```bash +# Verify the server is running +kubectl get mcpserver -n toolhive-system + +# Check the pods are healthy +kubectl get pods -n toolhive-system -l app.kubernetes.io/instance=fetch-telemetry +``` + + + + +## Step 3: Generate telemetry data + +Create some MCP interactions to generate traces and metrics for analysis. + + + + +### Connect your AI client + +Your MCP server is already configured to work with your AI client from the CLI +quickstart. Simply use your client to make requests that will generate telemetry +data. + + + + +### Port-forward to access the MCP server + +In a separate terminal window, create a port-forward to connect your AI client: + +```bash +kubectl port-forward service/mcp-fetch-telemetry-proxy -n toolhive-system 8080:8080 +``` + +Leave this running for the duration of this tutorial. + +### Configure your AI client + +Use the ToolHive CLI to add the MCP server to your client configuration: + +```bash +thv run http://localhost:8080/mcp --name fetch-k8s --transport streamable-http +``` + + + + +### Generate sample data + +Make several requests using your AI client to create diverse telemetry: + +1. **Basic fetch request**: "Fetch the content from https://toolhive.dev and + summarize it" +2. **Multiple requests**: Make 3-4 more fetch requests with different URLs +3. **Error generation**: Try an invalid URL to generate error traces + +Each interaction creates rich telemetry data including: + +- Request traces with timing information sent to Jaeger +- Tool call details with sanitized arguments +- Performance metrics sent to Prometheus + +The CLI and Kubernetes deployments will both generate similar telemetry data, +with the Kubernetes setup including additional Kubernetes-specific attributes. + +## Step 4: Access and analyze telemetry data + +Now examine your telemetry data using Jaeger and Prometheus to understand MCP +server performance. + + + + +### Access Jaeger for traces + +Open Jaeger in your browser at `http://localhost:16686`. + +### Explore traces in Jaeger + +1. In the **Service** dropdown, select `mcp-fetch-server` +2. Click **Find Traces** to see recent traces +3. Click on individual traces to see detailed spans + +Look for traces with protocol and MCP-specific attributes like: + +```json +{ + "serviceName": "mcp-fetch-server", + "http.duration_ms": "307.8", + "http.status_code": 200, + "mcp.method": "tools/call", + "mcp.tool.name": "fetch", + "mcp.tool.arguments": "url=https://toolhive.dev", + "mcp.transport": "streamable-http", + "service.version": "v0.3.6" +} +``` + +### Access Grafana for visualization + +Open `http://localhost:3000` in your browser and log in using the default +credentials (`admin` / `admin`). + +### Import the ToolHive dashboard + +1. Click the **+** icon in the top-right of the Grafana interface and select + **Import dashboard** +2. In the **Import via dashboard JSON** model input box, paste the contents of + [this example dashboard file](https://raw.githubusercontent.com/stacklok/toolhive/main/examples/otel/grafana-dashboards/toolhive-cli-mcp-grafana-dashboard-otel-scrape.json) +3. Click **Load**, then **Import** + +Make some requests to your MCP server again and watch the dashboard update in +real-time. + +You can also explore other metrics in Grafana by creating custom panels and +queries. See the +[Observability guide](../concepts/observability.mdx#grafana-dashboard-queries) +for examples. + + + + +### Port-forward to Jaeger + +Access Jaeger through a port-forward: + +```bash +kubectl port-forward service/jaeger-all-in-one-query -n monitoring 16686:16686 +``` + +Open `http://localhost:16686` in your browser. + +### Explore traces in Jaeger + +1. In the **Service** dropdown, select `mcp-fetch-server` +2. Click **Find Traces** to see recent traces +3. Click on individual traces to see detailed spans + +Review the available information including MCP and Kubernetes-specific +attributes like: + +```json +{ + "serviceName": "mcp-fetch-server", + "http.duration_ms": "307.8", + "http.status_code": 200, + "mcp.method": "tools/call", + "mcp.tool.name": "fetch", + "mcp.tool.arguments": "url=https://toolhive.dev", + "mcp.transport": "streamable-http", + "k8s.deployment.name": "fetch-telemetry", + "k8s.namespace.name": "toolhive-system", + "k8s.node.name": "toolhive-control-plane", + "k8s.pod.name": "fetch-telemetry-7d7d55687c-glvpz", + "service.namespace": "toolhive-system", + "service.version": "v0.3.6" +} +``` + +### Port-forward to Grafana + +Access Grafana through a port-forward: + +```bash +kubectl port-forward service/kube-prometheus-stack-grafana -n monitoring 3000:80 +``` + +Open `http://localhost:3000` in your browser and log in using the default +credentials (`admin` / `admin`). + +### Import the ToolHive dashboard + +1. Click the **+** icon in the top-right of the Grafana interface and select + **Import dashboard** +2. In the **Import via dashboard JSON** model input box, paste the contents of + [this example dashboard file](https://raw.githubusercontent.com/stacklok/toolhive/main/examples/otel/grafana-dashboards/toolhive-mcp-grafana-dashboard-otel-scrape.json) +3. Click **Load**, then **Import** + +Make some requests to your MCP server again and watch the dashboard update in +real-time. + +You can also explore other metrics in Grafana by creating custom panels and +queries. See the +[Observability guide](../concepts/observability.mdx#grafana-dashboard-queries) +for examples. + + + + +## Step 5: Cleanup + +When you're finished exploring, clean up your resources. + + + + +### Stop MCP servers + +```bash +# Stop and remove the MCP server +thv rm fetch + +# Clear telemetry configuration (optional) +thv config otel unset-endpoint +thv config otel unset-metrics-enabled +thv config otel unset-tracing-enabled +thv config otel unset-sampling-rate +thv config otel unset-insecure +``` + +### Stop observability stack + +```bash +# Stop all containers +docker compose -f observability-stack.yml down + +# Remove all data (optional) +docker compose -f observability-stack.yml down -v + +# Clean up provisioning directories (optional) +rm -rf grafana/ +``` + + + + +### Remove MCP servers + +```bash +# Delete the MCP server +kubectl delete mcpserver fetch-telemetry -n toolhive-system +``` + +### Remove observability stack + +```bash +# Delete observability components +helm uninstall otel-collector -n monitoring +helm uninstall kube-prometheus-stack -n monitoring +helm uninstall jaeger-all-in-one -n monitoring + +# Remove the monitoring namespace +kubectl delete namespace monitoring +``` + +### Optional: Remove the kind cluster + +If you're completely done: + +```bash +kind delete cluster --name toolhive +``` + + + + +## What's next? + +Congratulations! You've successfully set up comprehensive observability for +ToolHive MCP workloads using Jaeger and Prometheus. + +To learn more about ToolHive's telemetry capabilities and best practices, see +the [Observability concepts guide](../concepts/observability.mdx). + +Here are some next steps to explore: + +- **Custom dashboards**: Create Grafana dashboards that query both Jaeger and + Prometheus +- **Alerting**: Set up Prometheus AlertManager for performance and error alerts +- **Performance optimization**: Use telemetry data to optimize MCP server + performance +- **Distributed tracing**: Understand request flows across multiple MCP servers + + + + +### CLI-specific next steps + +- **Review the CLI telemetry guide**: Explore + [detailed configuration options](../guides-cli/telemetry-and-metrics.mdx) +- **Scale to multiple servers**: Run multiple MCP servers with different + configurations +- **Production CLI setup**: Learn about + [secrets management](../guides-cli/secrets-management.mdx) and + [custom permissions](../guides-cli/custom-permissions.mdx) +- **Alternative backends**: Try other observability platforms mentioned in the + [CLI telemetry guide](../guides-cli/telemetry-and-metrics.mdx) + + + + +### Kubernetes-specific next steps + +- **Review the Kubernetes telemetry guide**: Explore + [detailed configuration options](../guides-k8s/telemetry-and-metrics.mdx) +- **Production deployment**: Set up production-grade + [Jaeger](https://www.jaegertracing.io/docs/deployment/) and + [Prometheus](https://prometheus.io/docs/prometheus/latest/installation/) with + persistent storage, or configure an OpenTelemetry Collector to work with your + existing observability tools +- **Advanced MCP configurations**: Explore + [Kubernetes MCP deployment patterns](../guides-k8s/run-mcp-k8s.mdx) +- **Secrets integration**: Learn about + [HashiCorp Vault integration](./vault.mdx) +- **Service mesh observability**: Integrate with Istio or Linkerd for enhanced + tracing + + + + +## Related information + +- [Observability concepts](../concepts/observability.mdx) - Understanding + ToolHive's telemetry architecture +- [CLI telemetry guide](../guides-cli/telemetry-and-metrics.mdx) - Detailed CLI + configuration options +- [Kubernetes telemetry guide](../guides-k8s/telemetry-and-metrics.mdx) - + Kubernetes operator telemetry features +- [OpenTelemetry Collector documentation](https://opentelemetry.io/docs/collector/) - + Official OpenTelemetry Collector documentation +- [Jaeger documentation](https://www.jaegertracing.io/docs/) - Official Jaeger + documentation +- [Prometheus documentation](https://prometheus.io/docs/) - Official Prometheus + documentation + +## Troubleshooting + + + + +
+Docker containers won't start + +Check Docker daemon and container logs: + +```bash +# Verify Docker is running +docker info + +# Check container logs +docker compose -f observability-stack.yml logs jaeger +docker compose -f observability-stack.yml logs prometheus +docker compose -f observability-stack.yml logs otel-collector +``` + +Common issues: + +- Port conflicts with existing services +- Insufficient Docker memory allocation +- Missing configuration files + +
+ +
+ToolHive CLI not sending telemetry + +Verify telemetry configuration: + +```bash +# Check current config +thv config otel get-endpoint +thv config otel get-metrics-enabled + +``` + +Check the ToolHive proxy logs for telemetry export errors: + +```bash +thv logs fetch --proxy [--follow] +``` + +Alternatively, you can check the log file directly at: + +- **macOS**: `~/Library/Application Support/toolhive/logs/fetch.log` +- **Windows**: `%LOCALAPPDATA%\toolhive\logs\fetch.log` +- **Linux**: `~/.local/share/toolhive/logs/fetch.log` + +
+ +
+No traces in Jaeger + +Check the telemetry pipeline: + +1. **Verify collector is receiving data**: `curl http://localhost:8888/metrics` +2. **Check collector logs**: `docker logs otel-collector` +3. **Verify Jaeger connectivity**: `curl http://localhost:16686/api/services` + +
+ +
+ + +
+Pods stuck in pending state + +Check cluster resources and pod events: + +```bash +# Check pod status +kubectl get pods -n monitoring + +# Describe problematic pods +kubectl describe pod -n monitoring + +# Check node resources +kubectl top nodes +``` + +Common issues: + +- Insufficient cluster resources +- Image pull failures +- Network policies blocking communication + +
+ +
+MCP server not sending telemetry + +Verify the telemetry configuration and connectivity: + +```bash +# Check MCPServer status +kubectl describe mcpserver fetch-telemetry -n toolhive-system + +# Check OpenTelemetry Collector logs +kubectl logs deployment/otel-collector -n monitoring + +# Verify service connectivity +kubectl exec -it deployment/otel-collector -n monitoring -- wget -qO- http://jaeger:16686/api/services +``` + +
+ +
+
+ +
+No metrics in Prometheus + +Common troubleshooting steps: + +1. **Verify Prometheus targets**: Check `http://localhost:9090/targets` to + ensure `otel-collector` target is UP +2. **Check collector metrics endpoint**: `curl http://localhost:8889/metrics` + (CLI) or port-forward and check in K8s +3. **Review collector configuration**: Ensure the Prometheus exporter is + properly configured +4. **Check Prometheus config**: Verify the scrape configuration includes the + collector endpoint + +
diff --git a/versioned_docs/version-1.0/toolhive/integrations/vault.mdx b/versioned_docs/version-1.0/toolhive/integrations/vault.mdx new file mode 100644 index 00000000..56524ff1 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/integrations/vault.mdx @@ -0,0 +1,285 @@ +--- +title: HashiCorp Vault integration +description: + Learn how to securely manage MCP server secrets using HashiCorp Vault with + ToolHive Kubernetes Operator. +--- + +This tutorial shows how to integrate HashiCorp Vault with the ToolHive +Kubernetes Operator to securely manage secrets for your MCP servers. Using the +[Vault Agent Injector](https://developer.hashicorp.com/vault/docs/deploy/kubernetes/injector), +you can automatically provision secrets into MCP server pods without exposing +sensitive data in your Kubernetes manifests. + +To demonstrate this integration, you will deploy a GitHub MCP server that +retrieves a GitHub personal access token (PAT) from Vault. + +:::info[Prerequisites] + +Before starting this tutorial, ensure you have: + +- A Kubernetes cluster with the ToolHive Operator installed +- kubectl configured to access your cluster +- Helm 3.x installed +- Basic familiarity with HashiCorp Vault concepts +- A GitHub Personal Access Token (PAT) + +If you need help installing the ToolHive Operator, see the +[Kubernetes quickstart guide](../guides-k8s/quickstart.mdx). + +::: + +## Overview + +The integration works by using HashiCorp Vault's Agent Injector to automatically +inject secrets into MCP server pods. When you add specific annotations to your +MCPServer resource, the Vault Agent Injector: + +1. Detects the annotations and injects a Vault Agent sidecar +2. Authenticates with Vault using Kubernetes service account tokens of the + `proxyrunner` pod +3. Retrieves secrets from Vault and writes them to a shared volume +4. Makes the secrets available as environment variables to your MCP server pod + +## Step 1: Install and configure Vault + +First, install Vault with the Agent Injector enabled in your Kubernetes cluster. + +### Install Vault using Helm + +Add the HashiCorp Helm repository and install Vault: + +```bash +# Add HashiCorp Helm repository +helm repo add hashicorp https://helm.releases.hashicorp.com +helm repo update + +# Create vault namespace +kubectl create namespace vault + +# Install Vault with Agent Injector +helm install vault hashicorp/vault \ + --namespace vault \ + --set "server.dev.enabled=true" \ + --set "server.dev.devRootToken=dev-only-token" \ + --set "injector.enabled=true" +``` + +:::warning[Development setup only] + +This tutorial uses Vault in development mode (`server.dev.enabled=true`) with a +static root token for simplicity. **Do not use this configuration in +production**. For production deployments, follow the [Vault production hardening +guide][vault-hardening]. + +::: + +Wait for the Vault pod to be ready: + +```bash +kubectl wait --for=condition=ready pod vault-0 \ + --namespace vault \ + --timeout=300s +``` + +### Configure Vault authentication + +Configure Vault to authenticate Kubernetes service accounts: + +```bash +# Get the Vault pod name +VAULT_POD=$(kubectl get pods --namespace vault \ + -l app.kubernetes.io/name=vault \ + -o jsonpath="{.items[0].metadata.name}") + +# Enable Kubernetes auth method +kubectl exec --namespace vault "$VAULT_POD" -- \ + vault auth enable kubernetes + +# Configure Kubernetes auth +kubectl exec --namespace vault "$VAULT_POD" -- \ + vault write auth/kubernetes/config \ + kubernetes_host="https://kubernetes.default.svc:443" \ + kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt \ + token_reviewer_jwt=@/var/run/secrets/kubernetes.io/serviceaccount/token +``` + +### Set up secrets engine and policies + +Enable a key-value secrets engine and create the necessary policies: + +```bash +# Enable KV secrets engine +kubectl exec --namespace vault "$VAULT_POD" -- \ + vault secrets enable -path=workload-secrets kv-v2 + +# Create Vault policy for MCP workloads +kubectl exec --namespace vault "$VAULT_POD" -- \ + sh -c 'vault policy write toolhive-workload-secrets - << EOF +path "auth/token/lookup-self" { capabilities = ["read"] } +path "auth/token/renew-self" { capabilities = ["update"] } +path "workload-secrets/data/github-mcp/*" { capabilities = ["read"] } +EOF' + +# Create Kubernetes auth role +kubectl exec --namespace vault "$VAULT_POD" -- \ + vault write auth/kubernetes/role/toolhive-mcp-workloads \ + bound_service_account_names="*-proxy-runner,mcp-*" \ + bound_service_account_namespaces="toolhive-system" \ + policies="toolhive-workload-secrets" \ + audience="https://kubernetes.default.svc.cluster.local" \ + ttl="1h" \ + max_ttl="4h" +``` + +## Step 2: Store secrets in Vault + +Create secrets for your MCP servers in Vault. This example shows how to store a +GitHub personal access token: + +```bash +# Store GitHub MCP server configuration +kubectl exec --namespace vault "$VAULT_POD" -- \ + vault kv put workload-secrets/github-mcp/config \ + token="ghp_your_github_token_here" \ + organization="your-org" +``` + +You can verify the secret was stored correctly: + +```bash +kubectl exec --namespace vault "$VAULT_POD" -- \ + vault kv get workload-secrets/github-mcp/config +``` + +## Step 3: Configure your MCPServer resource + +Create an MCPServer resource with Vault annotations to enable automatic secret +injection. The key is using the `podTemplateMetadataOverrides` field to add +annotations to the proxy runner pods: + +```yaml title="github-mcp-with-vault.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: github-vault + namespace: toolhive-system +spec: + image: ghcr.io/github/github-mcp-server:latest + transport: stdio + proxyPort: 9095 + resources: + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' + resourceOverrides: + proxyDeployment: + podTemplateMetadataOverrides: + annotations: + # Enable Vault Agent injection + vault.hashicorp.com/agent-inject: 'true' + vault.hashicorp.com/role: 'toolhive-mcp-workloads' + + # Inject GitHub configuration secret + vault.hashicorp.com/agent-inject-secret-github-config: 'workload-secrets/data/github-mcp/config' + vault.hashicorp.com/agent-inject-template-github-config: | + {{- with secret "workload-secrets/data/github-mcp/config" -}} + GITHUB_PERSONAL_ACCESS_TOKEN={{ .Data.data.token }} + {{- end -}} +``` + +### Understanding the annotations + +The key annotations that enable Vault integration are: + +- `vault.hashicorp.com/agent-inject: "true"` - Enables Vault Agent injection for + this pod +- `vault.hashicorp.com/role: "toolhive-mcp-workloads"` - Specifies the Vault + role to use for authentication +- `vault.hashicorp.com/agent-inject-secret-github-config` - Tells Vault to + retrieve a secret and make it available as a file +- `vault.hashicorp.com/agent-inject-template-github-config` - Uses a Vault + template to format the secret as environment variables + +When ToolHive detects the `vault.hashicorp.com/agent-inject` annotation, it +automatically configures the proxy runner to read environment variables from the +`/vault/secrets/` directory where the Vault Agent writes the rendered templates. + +## Step 4: Deploy your MCPServer + +Apply your MCPServer configuration: + +```bash +kubectl apply -f github-mcp-with-vault.yaml +``` + +Monitor the deployment to ensure both the Vault Agent and ToolHive proxy runner +start successfully: + +```bash +# Watch the pod start up +kubectl get pods -n toolhive-system -w + +# Get the pod name +POD_NAME=$(kubectl get pods -n toolhive-system \ + -l app.kubernetes.io/instance=github-vault \ + -o jsonpath="{.items[0].metadata.name}") + +# Check pod logs +kubectl logs -n toolhive-system $POD_NAME -c vault-agent +kubectl logs -n toolhive-system $POD_NAME -c toolhive +``` + +You should see the Vault Agent successfully authenticate and retrieve secrets, +and the ToolHive proxy runner start with the injected environment variables. + +## Step 5: Verify the integration + +Test that your MCP server has access to the secrets by checking the running pod: + +```bash +# Get the proxy pod name - note the instance name is the same +# as the name of our MCPServer +PROXY_POD_NAME=$(kubectl get pods -n toolhive-system \ + -l app.kubernetes.io/instance=github-vault \ + -o jsonpath="{.items[0].metadata.name}") + +# Get the mcp server pod name - note the instance name is the same +# as the name of our MCPServer +MCP_POD_NAME=$(kubectl get pods -ntoolhive-system \ + -lapp=github-vault,toolhive-tool-type=mcp \ + -ojsonpath='{.items[0].metadata.name}') + +# Verify the Vault Agent wrote the secret file +kubectl exec -n toolhive-system "$PROXY_POD_NAME" -c vault-agent -- \ + cat /vault/secrets/github-config + +# Check that the environment variable is available to the MCP server +kubectl get pod $MCP_POD_NAME -n toolhive-system -o jsonpath='{range .spec.containers[?(@.name=="mcp")].env[*]}{.name}{"="}{.value}{"\n"}{end}' +``` + +## Security best practices + +:::tip[Production recommendations] + +- Use Vault in production mode with proper TLS certificates +- Implement least-privilege policies for secret access +- Enable audit logging in Vault +- Regularly rotate Vault tokens and secrets +- Monitor Vault Agent logs for authentication issues +- Use namespace isolation for different environments + +::: + +## Related information + +- [Kubernetes quickstart guide](../guides-k8s/quickstart.mdx) +- [Secrets management guide](../guides-cli/secrets-management.mdx) +- [HashiCorp Vault documentation](https://developer.hashicorp.com/vault/docs) +- [Vault Agent Injector documentation][vault-injector] + +[vault-hardening]: https://developer.hashicorp.com/vault/tutorials/operations/production-hardening +[vault-injector]: https://developer.hashicorp.com/vault/docs/platform/k8s/injector diff --git a/versioned_docs/version-1.0/toolhive/reference/api.mdx b/versioned_docs/version-1.0/toolhive/reference/api.mdx new file mode 100644 index 00000000..a06d6ee2 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/api.mdx @@ -0,0 +1,9 @@ +--- +title: API reference +description: Reference documentation for ToolHive's REST API +hide_table_of_contents: true +--- + +import ApiDocMdx from '@theme/ApiDocMdx'; + + diff --git a/versioned_docs/version-1.0/toolhive/reference/authz-policy-reference.mdx b/versioned_docs/version-1.0/toolhive/reference/authz-policy-reference.mdx new file mode 100644 index 00000000..4429debf --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/authz-policy-reference.mdx @@ -0,0 +1,495 @@ +--- +title: Authorization policy reference +description: Complete reference for Cedar entity types, actions, attributes, and + annotations available when writing ToolHive authorization policies. +--- + +This page lists the Cedar entity types, actions, attributes, and annotations +available when writing authorization policies for ToolHive MCP servers. It also +covers group membership and the HTTP PDP model for external policy decision +points. + +For conceptual guidance and practical examples, see +[Cedar policies](../concepts/cedar-policies.mdx). + +## Cedar entity types + +Every Cedar authorization request involves three entity types: a principal, an +action, and a resource. ToolHive maps MCP concepts to these Cedar entities +automatically. + +| Entity type | Format | Description | +| ------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Client` | `Client::""` | The authenticated user, identified by the `sub` claim from the access token | +| `Action` | `Action::""` | The MCP operation being performed | +| `Tool` | `Tool::""` | A tool resource (used for `tools/call`) | +| `Prompt` | `Prompt::""` | A prompt resource (used for `prompts/get`) | +| `Resource` | `Resource::""` | A data resource (used for `resources/read`). The URI is [sanitized](#resource-uri-sanitization) for Cedar compatibility | +| `FeatureType` | `FeatureType::""` | A feature category entity. Values: `tool`, `prompt`, `resource`. Not currently used for authorization; list operations are handled via [response filtering](#list-operation-filtering) | +| `THVGroup` | `THVGroup::""` | A group membership entity. Used with Cedar's `in` operator for [group-based policies](#group-membership) | + +## Cedar actions + +ToolHive maps MCP methods to Cedar actions. Each action corresponds to a +specific MCP operation. + +### Actions that require authorization + +These actions are evaluated against your Cedar policies: + +| Action | MCP method | Description | +| ------------------------- | ---------------- | ----------------------------- | +| `Action::"call_tool"` | `tools/call` | Call a specific tool | +| `Action::"get_prompt"` | `prompts/get` | Retrieve a specific prompt | +| `Action::"read_resource"` | `resources/read` | Read a specific data resource | + +### List operations + +List methods (`tools/list`, `prompts/list`, `resources/list`) bypass +request-level authorization entirely. ToolHive allows the list request through +and filters the response to include only items the caller is authorized to +access using the individual-access actions above. See +[List operation filtering](#list-operation-filtering) for details. + +### Always-allowed MCP methods + +These MCP methods bypass authorization entirely. You cannot write policies to +restrict them: + +| MCP method | Purpose | +| --------------------- | ---------------------------------- | +| `initialize` | Protocol initialization handshake | +| `ping` | Health check | +| `features/list` | Capability discovery | +| `roots/list` | Root directory discovery | +| `logging/setLevel` | Client logging preference | +| `completion/complete` | Argument auto-completion | +| `notifications/*` | All server-to-client notifications | + +### Denied-by-default MCP methods + +These MCP methods are not in the authorization map and are always denied. They +require new authorization features before they can be enabled: + +- `elicitation/create` -- User input prompting +- `sampling/createMessage` -- LLM text generation +- `tasks/list`, `tasks/get`, `tasks/cancel`, `tasks/result` -- Task management + +## Principal attributes + +The principal entity (`Client::`) receives all JWT claims from the access token +with a `claim_` prefix. Any claim in the token becomes an attribute you can +reference in policies. + +### Common principal attributes + +| Attribute | Source | Cedar type | Description | +| -------------- | ------------ | -------------- | ----------------------------------------------------------------- | +| `claim_sub` | JWT `sub` | String | Subject identifier (also used as the entity ID) | +| `claim_name` | JWT `name` | String | Display name | +| `claim_email` | JWT `email` | String | Email address | +| `claim_roles` | JWT `roles` | Set of Strings | Role memberships | +| `claim_groups` | JWT `groups` | Set of Strings | Group memberships | +| `claim_role` | JWT `role` | String | Single role (some identity providers use this instead of `roles`) | +| `claim_` | JWT `` | Varies | Any other JWT claim | + +:::tip + +The exact attributes available depend on your identity provider and token +configuration. Check your access token's claims to see what's available. Every +claim becomes a `claim_`-prefixed attribute automatically. + +::: + +### Claim type mapping + +| JWT claim type | Cedar type | +| -------------------- | ------------------------------------------- | +| String | `String` | +| Boolean | `Bool` | +| Integer | `Long` | +| Float | `Decimal` | +| Array of strings | `Set` of `String` values | +| Array of mixed types | `Set` (each element converted individually) | + +## Resource attributes + +Resource attributes vary depending on the type of MCP operation. Each operation +type provides a different set of attributes on the resource entity. + +### Tool call attributes (`tools/call`) + +When a client calls a tool, the resource entity (`Tool::`) has these attributes: + +| Attribute | Type | Description | +| ----------------- | ------ | ------------------------------------------------------------------------------- | +| `name` | String | The tool name | +| `operation` | String | Always `"call"` | +| `feature` | String | Always `"tool"` | +| `readOnlyHint` | Bool | From [tool annotations](#tool-annotation-attributes), if the MCP server sets it | +| `destructiveHint` | Bool | From tool annotations, if set | +| `idempotentHint` | Bool | From tool annotations, if set | +| `openWorldHint` | Bool | From tool annotations, if set | +| `arg_` | Varies | Tool argument values (see [argument preprocessing](#argument-preprocessing)) | + +### Prompt get attributes (`prompts/get`) + +When a client retrieves a prompt, the resource entity (`Prompt::`) has these +attributes: + +| Attribute | Type | Description | +| ----------- | ------ | ---------------------- | +| `name` | String | The prompt name | +| `operation` | String | Always `"get"` | +| `feature` | String | Always `"prompt"` | +| `arg_` | Varies | Prompt argument values | + +### Resource read attributes (`resources/read`) + +When a client reads a data resource, the resource entity (`Resource::`) has +these attributes: + +| Attribute | Type | Description | +| ----------- | ------ | ----------------------------------------- | +| `name` | String | The sanitized URI (same as the entity ID) | +| `uri` | String | The original, unsanitized resource URI | +| `operation` | String | Always `"read"` | +| `feature` | String | Always `"resource"` | +| `arg_` | Varies | Request argument values | + +### Feature list attributes (list operations) + +:::info[Not currently used] + +The `FeatureType` entity and its attributes are defined in the Cedar authorizer +but are not evaluated during normal request processing. List operations bypass +request-level authorization and use +[response filtering](#list-operation-filtering) instead. This section is +included for completeness. + +::: + +When a client lists tools, prompts, or resources, the `FeatureType::` entity has +these attributes: + +| Attribute | Type | Description | +| ----------- | ------ | ------------------------------------------------------- | +| `name` | String | The feature type (same as the entity ID) | +| `type` | String | The feature type: `"tool"`, `"prompt"`, or `"resource"` | +| `operation` | String | Always `"list"` | +| `feature` | String | The feature type | + +## Tool annotation attributes + +MCP servers can declare behavioral hints on their tools through +[annotations](https://modelcontextprotocol.io/docs/specification/2025-06-18/server/tools#annotations). +ToolHive caches these annotations from `tools/list` responses and makes them +available as resource attributes during `tools/call` authorization. + +| Attribute | Type | Meaning when `true` | Meaning when `false` | +| ----------------- | ---- | -------------------------------------------------------------------------------- | -------------------------------------------------------------- | +| `readOnlyHint` | Bool | The tool only reads data; it does not modify anything | The tool may modify data | +| `destructiveHint` | Bool | The tool may perform destructive or irreversible operations | The tool's modifications are non-destructive or reversible | +| `idempotentHint` | Bool | Calling the tool multiple times with the same arguments produces the same result | Repeated calls may have different effects | +| `openWorldHint` | Bool | The tool interacts with external systems outside the MCP server's control | The tool operates only within a closed, controlled environment | + +:::warning[Annotations may be absent] + +Not all MCP servers set all annotation fields. An annotation attribute is only +present on the resource entity when the MCP server explicitly sets it. If a tool +omits an annotation, that attribute does not exist on the entity. + +Always use Cedar's `has` operator to check for the presence of an annotation +before accessing its value. Without `has`, accessing a missing attribute causes +a Cedar evaluation error, which ToolHive treats as a deny. + +::: + +### The `has` operator + +The `has` operator is essential for writing safe annotation-based policies. It +checks whether an attribute exists on an entity before you try to read it: + +```text +// Safe: checks existence before access +permit( + principal, + action == Action::"call_tool", + resource +) when { + resource has readOnlyHint && resource.readOnlyHint == true +}; +``` + +```text +// Unsafe: fails with an evaluation error if readOnlyHint is absent +permit( + principal, + action == Action::"call_tool", + resource +) when { + resource.readOnlyHint == true +}; +``` + +### Trust boundary + +Annotations are sourced exclusively from the MCP server's `tools/list` response, +not from the client's `tools/call` request. This prevents a malicious client +from setting `readOnlyHint: true` on a destructive tool to bypass +annotation-based policies. + +## Context attributes + +The Cedar context record contains a merged copy of all JWT claims and tool +arguments. This gives you an alternative way to reference these values in +policies. Context attributes use the same prefixes as entity attributes: + +| Prefix | Source | Example | +| ------------- | --------------------- | -------------------------------------------- | +| `claim_` | JWT claims | `context.claim_email == "admin@example.com"` | +| `arg_` | Tool/prompt arguments | `context.arg_location == "New York"` | + +You can use either entity attributes or context attributes in your policies. +Both contain the same values: + +```text +// These are equivalent: +principal.claim_roles.contains("admin") +context.claim_roles.contains("admin") + +// These are also equivalent: +resource.arg_location == "New York" +context.arg_location == "New York" +``` + +## Group membership + +ToolHive automatically extracts group claims from JWT tokens and creates +`THVGroup` parent entities for the principal. This lets you write group-based +policies using Cedar's `in` operator. + +### How groups are resolved + +ToolHive checks the following JWT claim names in order and uses the first one +found: + +1. Custom claim name (if configured via `group_claim_name` in Cedar config) +2. `groups` -- Microsoft Entra ID, Okta, Auth0, PingIdentity +3. `roles` -- Keycloak +4. `cognito:groups` -- AWS Cognito + +The claim value must be an array of strings. Each string becomes a `THVGroup` +entity, and the principal is added as a child of each group. + +### Group policy examples + +```text +// Allow members of the "engineering" group to call any tool +permit( + principal in THVGroup::"engineering", + action == Action::"call_tool", + resource +); +``` + +```text +// Allow only the "platform" group to read infrastructure resources +permit( + principal in THVGroup::"platform", + action == Action::"read_resource", + resource +); +``` + +### Configuring a custom group claim + +If your identity provider uses a non-standard claim name for groups (for +example, Auth0 namespaced claims), configure it in the Cedar authorization +config: + +```yaml title="authz-config.yaml" +version: '1.0' +type: cedarv1 +cedar: + group_claim_name: 'https://example.com/groups' + policies: + - 'permit(principal in THVGroup::"admins", action, resource);' + entities_json: '[]' +``` + +## Argument preprocessing + +Tool and prompt arguments are converted to Cedar-compatible types with an `arg_` +prefix. The conversion rules depend on the argument's Go type: + +| Argument type | Cedar attribute | Cedar type | Example | +| ----------------------- | ------------------- | -------------------- | ------------------------------------- | +| String | `arg_` | String | `resource.arg_location == "NYC"` | +| Boolean | `arg_` | Bool | `resource.arg_verbose == true` | +| Integer | `arg_` | Long | `resource.arg_limit == 10` | +| Float | `arg_` | Decimal | `resource.arg_threshold == 0.95` | +| Complex (object, array) | `arg__present` | Bool (always `true`) | `resource.arg_config_present == true` | + +Complex argument types (objects, nested arrays) cannot be represented directly +in Cedar. Instead, ToolHive creates a boolean `arg__present` attribute set +to `true`, which lets you check whether the argument was provided without +inspecting its value. + +## Resource URI sanitization + +For `resources/read` operations, the resource URI is sanitized to create a valid +Cedar entity ID. The following characters are replaced with underscores (`_`): +`:`, `/`, `\`, `?`, `&`, `=`, `#`, `.`, and ` ` (space). + +For example, the URI `file:///data/config.json` becomes the entity ID +`Resource::"file____data_config_json"`. + +To write policies against resource URIs, use the unsanitized `uri` attribute +instead of matching the entity ID directly: + +```text +// Use the uri attribute for readable policies +permit( + principal, + action == Action::"read_resource", + resource +) when { + resource.uri == "file:///data/config.json" +}; +``` + +## List operation filtering + +List operations (`tools/list`, `prompts/list`, `resources/list`) bypass +request-level authorization entirely. ToolHive forwards the list request to the +MCP server, then filters the response to include only items the caller is +authorized to access. + +For each item in the list response, ToolHive runs a policy check using the +corresponding individual-access action: + +| List method | Per-item check uses | +| ---------------- | -------------------------------------------------------------------- | +| `tools/list` | `Action::"call_tool"` against each `Tool::""` | +| `prompts/list` | `Action::"get_prompt"` against each `Prompt::""` | +| `resources/list` | `Action::"read_resource"` against each `Resource::""` | + +This means you don't need separate list policies. Your `call_tool`, +`get_prompt`, and `read_resource` policies automatically control what appears in +list responses. For resources, the per-item check uses the +[sanitized](#resource-uri-sanitization) entity ID, while the original URI +remains available via the `resource.uri` attribute. + +:::note + +Because list responses are filtered using `call_tool`, `get_prompt`, and +`read_resource` policies, an item only appears in a list response when the +corresponding individual-access policy permits it. For example, if no +`call_tool` policy permits a given tool, that tool won't appear in `tools/list` +responses. + +::: + +## Custom static entities + +You can define custom entities with arbitrary attributes using the +`entities_json` field in your Cedar configuration. These entities are merged +with the dynamically created entities at evaluation time. This lets you attach +metadata to tools, prompts, or resources that you can reference in policies. + +```yaml title="authz-config.yaml" +version: '1.0' +type: cedarv1 +cedar: + policies: + - | + permit( + principal, + action == Action::"call_tool", + resource + ) when { + resource.owner == principal.claim_sub + }; + entities_json: | + [ + { + "uid": "Tool::weather", + "attrs": { + "owner": "user123", + "department": "engineering" + } + }, + { + "uid": "Tool::billing", + "attrs": { + "owner": "finance-bot", + "department": "finance" + } + } + ] +``` + +:::warning + +Static entity attributes are merged with dynamic attributes at evaluation time. +The dynamic attributes (`name`, `operation`, `feature`, and any `arg_` or +annotation attributes) always take precedence over static ones. Don't define +static attributes using reserved names. + +::: + +## HTTP PDP PORC mapping + +The HTTP PDP authorizer (`httpv1`) maps MCP requests to a PORC +(Principal-Operation-Resource-Context) model for external policy decision +points. + +### PORC fields + +| Field | Format | Example | +| ----------------------------------------- | -------------------------------------------------- | --------------------------------- | +| `principal.sub` | JWT `sub` claim | `"user@example.com"` | +| `principal.roles` or `principal.mroles` | Depends on [claim mapper](#http-pdp-claim-mappers) | `["developer"]` | +| `principal.groups` or `principal.mgroups` | Depends on claim mapper | `["engineering"]` | +| `principal.scopes` | JWT `scope` or `scopes` | `["read", "write"]` | +| `operation` | `mcp::` | `"mcp:tool:call"` | +| `resource` | `mrn:mcp:::` | `"mrn:mcp:myserver:tool:weather"` | + +### PORC context fields + +Context fields are optional and controlled by the `context` configuration: + +| Field | Config required | Description | +| ----------------------------------------- | ----------------------------- | --------------------- | +| `context.mcp.feature` | `include_operation: true` | MCP feature type | +| `context.mcp.operation` | `include_operation: true` | MCP operation type | +| `context.mcp.resource_id` | `include_operation: true` | Resource identifier | +| `context.mcp.args.` | `include_args: true` | Tool/prompt arguments | +| `context.mcp.annotations.readOnlyHint` | Automatic for tool operations | Tool annotation hint | +| `context.mcp.annotations.destructiveHint` | Automatic for tool operations | Tool annotation hint | +| `context.mcp.annotations.idempotentHint` | Automatic for tool operations | Tool annotation hint | +| `context.mcp.annotations.openWorldHint` | Automatic for tool operations | Tool annotation hint | + +### HTTP PDP claim mappers + +| Mapper | Config value | Principal fields | Compatible with | +| ------------- | --------------------------- | ------------------------------------------------------------------ | ------------------------------------------- | +| MPE | `claim_mapping: "mpe"` | `sub`, `mroles`, `mgroups`, `scopes`, `mclearance`, `mannotations` | Manetu PolicyEngine | +| Standard OIDC | `claim_mapping: "standard"` | `sub`, `roles`, `groups`, `scopes` | Generic PDPs expecting standard OIDC claims | + +## Next steps + +- Learn practical policy patterns and profiles in + [Cedar policies](../concepts/cedar-policies.mdx) +- Set up authorization for [CLI-managed MCP servers](../guides-cli/auth.mdx) or + [Kubernetes-deployed MCP servers](../guides-k8s/auth-k8s.mdx) +- Follow the end-to-end + [Role-based authorization with Okta](../integrations/okta.mdx) tutorial + +## Related information + +- [Authentication and authorization](../concepts/auth-framework.mdx) -- Overview + of the authentication and authorization framework +- [Cedar documentation](https://docs.cedarpolicy.com/) -- Official Cedar policy + language reference diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv.md new file mode 100644 index 00000000..1574d72a --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv.md @@ -0,0 +1,58 @@ +--- +title: thv +hide_title: true +description: Reference for ToolHive CLI command `thv` +last_update: + author: autogenerated +slug: thv +mdx: + format: md +--- + +## thv + +ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers + +### Synopsis + +ToolHive (thv) is a lightweight, secure, and fast manager for MCP (Model Context Protocol) servers. +It is written in Go and has extensive test coverage—including input validation—to ensure reliability and security. + +Under the hood, ToolHive acts as a very thin client for the Docker/Podman/Colima Unix socket API. +This design choice allows it to remain both efficient and lightweight while still providing powerful, +container-based isolation for running MCP servers. + +``` +thv [flags] +``` + +### Options + +``` + --debug Enable debug mode + -h, --help help for thv +``` + +### SEE ALSO + +- [thv build](thv_build.md) - Build a container for an MCP server without running it +- [thv client](thv_client.md) - Manage MCP clients +- [thv config](thv_config.md) - Manage application configuration +- [thv export](thv_export.md) - Export a workload's run configuration to a file +- [thv group](thv_group.md) - Manage logical groupings of MCP servers +- [thv inspector](thv_inspector.md) - Launches the MCP Inspector UI and connects it to the specified MCP server +- [thv list](thv_list.md) - List running MCP servers +- [thv logs](thv_logs.md) - Output the logs of an MCP server or manage log files +- [thv mcp](thv_mcp.md) - Interact with MCP servers for debugging +- [thv proxy](thv_proxy.md) - Create a transparent proxy for an MCP server with authentication support +- [thv registry](thv_registry.md) - Manage MCP server registry +- [thv rm](thv_rm.md) - Remove one or more MCP servers +- [thv run](thv_run.md) - Run an MCP server +- [thv runtime](thv_runtime.md) - Commands related to the container runtime +- [thv search](thv_search.md) - Search for MCP servers +- [thv secret](thv_secret.md) - Manage secrets +- [thv serve](thv_serve.md) - Start the ToolHive API server +- [thv start](thv_start.md) - Start (resume) a tooling server +- [thv status](thv_status.md) - Show detailed status of an MCP server +- [thv stop](thv_stop.md) - Stop one or more MCP servers +- [thv version](thv_version.md) - Show the version of ToolHive diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_build.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_build.md new file mode 100644 index 00000000..149b6c70 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_build.md @@ -0,0 +1,70 @@ +--- +title: thv build +hide_title: true +description: Reference for ToolHive CLI command `thv build` +last_update: + author: autogenerated +slug: thv_build +mdx: + format: md +--- + +## thv build + +Build a container for an MCP server without running it + +### Synopsis + +Build a container for an MCP server using a protocol scheme without running it. + +ToolHive supports building containers from protocol schemes: + + $ thv build uvx://package-name + $ thv build npx://package-name + $ thv build go://package-name + $ thv build go://./local-path + +Automatically generates a container that can run the specified package +using either uvx (Python with uv package manager), npx (Node.js), +or go (Golang). For Go, you can also specify local paths starting +with './' or '../' to build local Go projects. + +Build-time arguments can be baked into the container's ENTRYPOINT: + + $ thv build npx://@launchdarkly/mcp-server -- start + $ thv build uvx://package -- --transport stdio + +These arguments become part of the container image and will always run, +with runtime arguments (from 'thv run -- ') appending after them. + +The container will be built and tagged locally, ready to be used with 'thv run' +or other container tools. The built image name will be displayed upon successful completion. + +Examples: +$ thv build uvx://mcp-server-git +$ thv build --tag my-custom-name:latest npx://@modelcontextprotocol/server-filesystem +$ thv build go://./my-local-server +$ thv build npx://@launchdarkly/mcp-server -- start + +``` +thv build [flags] PROTOCOL [-- ARGS...] +``` + +### Options + +``` + --dry-run Generate Dockerfile without building (stdout output unless -o is set) (default false) + -h, --help help for build + -o, --output string Write the Dockerfile to the specified file instead of building (default builds an image instead of generating a Dockerfile) + -t, --tag string Name and optionally a tag in the 'name:tag' format for the built image (default generates a unique image name based on the package and transport type) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_client.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_client.md new file mode 100644 index 00000000..35c9879d --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_client.md @@ -0,0 +1,39 @@ +--- +title: thv client +hide_title: true +description: Reference for ToolHive CLI command `thv client` +last_update: + author: autogenerated +slug: thv_client +mdx: + format: md +--- + +## thv client + +Manage MCP clients + +### Synopsis + +The client command provides subcommands to manage MCP client integrations. + +### Options + +``` + -h, --help help for client +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers +- [thv client list-registered](thv_client_list-registered.md) - List all registered MCP clients +- [thv client register](thv_client_register.md) - Register a client for MCP server configuration +- [thv client remove](thv_client_remove.md) - Remove a client from MCP server configuration +- [thv client setup](thv_client_setup.md) - Interactively setup and register installed clients +- [thv client status](thv_client_status.md) - Show status of all supported MCP clients diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_client_list-registered.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_client_list-registered.md new file mode 100644 index 00000000..f104aaa3 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_client_list-registered.md @@ -0,0 +1,38 @@ +--- +title: thv client list-registered +hide_title: true +description: Reference for ToolHive CLI command `thv client list-registered` +last_update: + author: autogenerated +slug: thv_client_list-registered +mdx: + format: md +--- + +## thv client list-registered + +List all registered MCP clients + +### Synopsis + +List all clients that are registered for MCP server configuration. + +``` +thv client list-registered [flags] +``` + +### Options + +``` + -h, --help help for list-registered +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv client](thv_client.md) - Manage MCP clients diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_client_register.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_client_register.md new file mode 100644 index 00000000..1ac71f68 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_client_register.md @@ -0,0 +1,67 @@ +--- +title: thv client register +hide_title: true +description: Reference for ToolHive CLI command `thv client register` +last_update: + author: autogenerated +slug: thv_client_register +mdx: + format: md +--- + +## thv client register + +Register a client for MCP server configuration + +### Synopsis + +Register a client for MCP server configuration. + +Valid clients: + +- amp-cli: Sourcegraph Amp CLI +- amp-cursor: Cursor Sourcegraph Amp extension +- amp-vscode: VS Code Sourcegraph Amp extension +- amp-vscode-insider: VS Code Insiders Sourcegraph Amp extension +- amp-windsurf: Windsurf Sourcegraph Amp extension +- antigravity: Google Antigravity IDE +- claude-code: Claude Code CLI +- cline: VS Code Cline extension +- codex: OpenAI Codex CLI +- continue: Continue.dev IDE plugins +- cursor: Cursor editor +- gemini-cli: Google Gemini CLI +- goose: Goose AI agent +- kiro: Kiro AI IDE +- lm-studio: LM Studio application +- mistral-vibe: Mistral Vibe IDE +- opencode: OpenCode editor +- roo-code: VS Code Roo Code extension +- trae: Trae IDE +- vscode: Visual Studio Code +- vscode-insider: Visual Studio Code Insiders +- vscode-server: Microsoft's VS Code Server (remote development) +- windsurf: Windsurf IDE +- windsurf-jetbrains: Windsurf plugin for JetBrains IDEs +- zed: Zed editor + +``` +thv client register [client] [flags] +``` + +### Options + +``` + --group strings Only register workloads from specified groups (default [default]) + -h, --help help for register +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv client](thv_client.md) - Manage MCP clients diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_client_remove.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_client_remove.md new file mode 100644 index 00000000..89abd07d --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_client_remove.md @@ -0,0 +1,67 @@ +--- +title: thv client remove +hide_title: true +description: Reference for ToolHive CLI command `thv client remove` +last_update: + author: autogenerated +slug: thv_client_remove +mdx: + format: md +--- + +## thv client remove + +Remove a client from MCP server configuration + +### Synopsis + +Remove a client from MCP server configuration. + +Valid clients: + +- amp-cli: Sourcegraph Amp CLI +- amp-cursor: Cursor Sourcegraph Amp extension +- amp-vscode: VS Code Sourcegraph Amp extension +- amp-vscode-insider: VS Code Insiders Sourcegraph Amp extension +- amp-windsurf: Windsurf Sourcegraph Amp extension +- antigravity: Google Antigravity IDE +- claude-code: Claude Code CLI +- cline: VS Code Cline extension +- codex: OpenAI Codex CLI +- continue: Continue.dev IDE plugins +- cursor: Cursor editor +- gemini-cli: Google Gemini CLI +- goose: Goose AI agent +- kiro: Kiro AI IDE +- lm-studio: LM Studio application +- mistral-vibe: Mistral Vibe IDE +- opencode: OpenCode editor +- roo-code: VS Code Roo Code extension +- trae: Trae IDE +- vscode: Visual Studio Code +- vscode-insider: Visual Studio Code Insiders +- vscode-server: Microsoft's VS Code Server (remote development) +- windsurf: Windsurf IDE +- windsurf-jetbrains: Windsurf plugin for JetBrains IDEs +- zed: Zed editor + +``` +thv client remove [client] [flags] +``` + +### Options + +``` + --group strings Remove client from specified groups (if not set, removes all workloads from the client) + -h, --help help for remove +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv client](thv_client.md) - Manage MCP clients diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_client_setup.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_client_setup.md new file mode 100644 index 00000000..8050b90e --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_client_setup.md @@ -0,0 +1,38 @@ +--- +title: thv client setup +hide_title: true +description: Reference for ToolHive CLI command `thv client setup` +last_update: + author: autogenerated +slug: thv_client_setup +mdx: + format: md +--- + +## thv client setup + +Interactively setup and register installed clients + +### Synopsis + +Presents a list of installed but unregistered clients for interactive selection and registration. + +``` +thv client setup [flags] +``` + +### Options + +``` + -h, --help help for setup +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv client](thv_client.md) - Manage MCP clients diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_client_status.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_client_status.md new file mode 100644 index 00000000..b6566ea4 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_client_status.md @@ -0,0 +1,38 @@ +--- +title: thv client status +hide_title: true +description: Reference for ToolHive CLI command `thv client status` +last_update: + author: autogenerated +slug: thv_client_status +mdx: + format: md +--- + +## thv client status + +Show status of all supported MCP clients + +### Synopsis + +Display the installation and registration status of all supported MCP clients in a table format. + +``` +thv client status [flags] +``` + +### Options + +``` + -h, --help help for status +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv client](thv_client.md) - Manage MCP clients diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config.md new file mode 100644 index 00000000..86ba35b2 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config.md @@ -0,0 +1,50 @@ +--- +title: thv config +hide_title: true +description: Reference for ToolHive CLI command `thv config` +last_update: + author: autogenerated +slug: thv_config +mdx: + format: md +--- + +## thv config + +Manage application configuration + +### Synopsis + +The config command provides subcommands to manage application configuration settings. + +### Options + +``` + -h, --help help for config +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers +- [thv config get-build-auth-file](thv_config_get-build-auth-file.md) - Get build auth file configuration +- [thv config get-build-env](thv_config_get-build-env.md) - Get build environment variables +- [thv config get-ca-cert](thv_config_get-ca-cert.md) - Get the currently configured CA certificate path +- [thv config get-registry](thv_config_get-registry.md) - Get the currently configured registry +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration +- [thv config set-build-auth-file](thv_config_set-build-auth-file.md) - Set an auth file for protocol builds +- [thv config set-build-env](thv_config_set-build-env.md) - Set a build environment variable for protocol builds +- [thv config set-ca-cert](thv_config_set-ca-cert.md) - Set the default CA certificate for container builds +- [thv config set-registry](thv_config_set-registry.md) - Set the MCP server registry +- [thv config set-registry-auth](thv_config_set-registry-auth.md) - Configure OAuth/OIDC authentication for the registry +- [thv config unset-build-auth-file](thv_config_unset-build-auth-file.md) - Remove build auth file(s) +- [thv config unset-build-env](thv_config_unset-build-env.md) - Remove build environment variable(s) +- [thv config unset-ca-cert](thv_config_unset-ca-cert.md) - Remove the configured CA certificate +- [thv config unset-registry](thv_config_unset-registry.md) - Remove the configured registry +- [thv config unset-registry-auth](thv_config_unset-registry-auth.md) - Remove registry authentication configuration +- [thv config usage-metrics](thv_config_usage-metrics.md) - Enable or disable anonymous usage metrics diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_get-build-auth-file.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_get-build-auth-file.md new file mode 100644 index 00000000..f2db71f7 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_get-build-auth-file.md @@ -0,0 +1,49 @@ +--- +title: thv config get-build-auth-file +hide_title: true +description: Reference for ToolHive CLI command `thv config get-build-auth-file` +last_update: + author: autogenerated +slug: thv_config_get-build-auth-file +mdx: + format: md +--- + +## thv config get-build-auth-file + +Get build auth file configuration + +### Synopsis + +Display configured build auth files. +If a name is provided, shows only that specific file. +If no name is provided, shows all configured files. + +By default, file contents are hidden to prevent credential exposure. +Use --show-content to display the actual content. + +Examples: +thv config get-build-auth-file # Show all files (content hidden) +thv config get-build-auth-file npmrc # Show specific file (content hidden) +thv config get-build-auth-file npmrc --show-content # Show with content + +``` +thv config get-build-auth-file [name] [flags] +``` + +### Options + +``` + -h, --help help for get-build-auth-file + --show-content Show the actual file content (contains credentials) (default false) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_get-build-env.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_get-build-env.md new file mode 100644 index 00000000..9228fbbf --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_get-build-env.md @@ -0,0 +1,44 @@ +--- +title: thv config get-build-env +hide_title: true +description: Reference for ToolHive CLI command `thv config get-build-env` +last_update: + author: autogenerated +slug: thv_config_get-build-env +mdx: + format: md +--- + +## thv config get-build-env + +Get build environment variables + +### Synopsis + +Display configured build environment variables. +If a KEY is provided, shows only that specific variable. +If no KEY is provided, shows all configured variables. + +Examples: +thv config get-build-env # Show all variables +thv config get-build-env NPM_CONFIG_REGISTRY # Show specific variable + +``` +thv config get-build-env [KEY] [flags] +``` + +### Options + +``` + -h, --help help for get-build-env +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_get-ca-cert.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_get-ca-cert.md new file mode 100644 index 00000000..c724c654 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_get-ca-cert.md @@ -0,0 +1,38 @@ +--- +title: thv config get-ca-cert +hide_title: true +description: Reference for ToolHive CLI command `thv config get-ca-cert` +last_update: + author: autogenerated +slug: thv_config_get-ca-cert +mdx: + format: md +--- + +## thv config get-ca-cert + +Get the currently configured CA certificate path + +### Synopsis + +Display the path to the CA certificate file that is currently configured for container builds. + +``` +thv config get-ca-cert [flags] +``` + +### Options + +``` + -h, --help help for get-ca-cert +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_get-registry.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_get-registry.md new file mode 100644 index 00000000..494d79e5 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_get-registry.md @@ -0,0 +1,38 @@ +--- +title: thv config get-registry +hide_title: true +description: Reference for ToolHive CLI command `thv config get-registry` +last_update: + author: autogenerated +slug: thv_config_get-registry +mdx: + format: md +--- + +## thv config get-registry + +Get the currently configured registry + +### Synopsis + +Display the currently configured registry (URL or file path). + +``` +thv config get-registry [flags] +``` + +### Options + +``` + -h, --help help for get-registry +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel.md new file mode 100644 index 00000000..707c6d14 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel.md @@ -0,0 +1,55 @@ +--- +title: thv config otel +hide_title: true +description: Reference for ToolHive CLI command `thv config otel` +last_update: + author: autogenerated +slug: thv_config_otel +mdx: + format: md +--- + +## thv config otel + +Manage OpenTelemetry configuration + +### Synopsis + +Configure OpenTelemetry settings for observability and monitoring of MCP servers. + +### Options + +``` + -h, --help help for otel +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration +- [thv config otel get-enable-prometheus-metrics-path](thv_config_otel_get-enable-prometheus-metrics-path.md) - Get the currently configured OpenTelemetry Prometheus metrics path flag +- [thv config otel get-endpoint](thv_config_otel_get-endpoint.md) - Get the currently configured OpenTelemetry endpoint +- [thv config otel get-env-vars](thv_config_otel_get-env-vars.md) - Get the currently configured OpenTelemetry environment variables +- [thv config otel get-insecure](thv_config_otel_get-insecure.md) - Get the currently configured OpenTelemetry insecure transport flag +- [thv config otel get-metrics-enabled](thv_config_otel_get-metrics-enabled.md) - Get the currently configured OpenTelemetry metrics export flag +- [thv config otel get-sampling-rate](thv_config_otel_get-sampling-rate.md) - Get the currently configured OpenTelemetry sampling rate +- [thv config otel get-tracing-enabled](thv_config_otel_get-tracing-enabled.md) - Get the currently configured OpenTelemetry tracing export flag +- [thv config otel set-enable-prometheus-metrics-path](thv_config_otel_set-enable-prometheus-metrics-path.md) - Set the OpenTelemetry Prometheus metrics path flag +- [thv config otel set-endpoint](thv_config_otel_set-endpoint.md) - Set the OpenTelemetry endpoint URL +- [thv config otel set-env-vars](thv_config_otel_set-env-vars.md) - Set the OpenTelemetry environment variables +- [thv config otel set-insecure](thv_config_otel_set-insecure.md) - Set the OpenTelemetry insecure transport flag +- [thv config otel set-metrics-enabled](thv_config_otel_set-metrics-enabled.md) - Set the OpenTelemetry metrics export to enabled +- [thv config otel set-sampling-rate](thv_config_otel_set-sampling-rate.md) - Set the OpenTelemetry sampling rate +- [thv config otel set-tracing-enabled](thv_config_otel_set-tracing-enabled.md) - Set the OpenTelemetry tracing export to enabled +- [thv config otel unset-enable-prometheus-metrics-path](thv_config_otel_unset-enable-prometheus-metrics-path.md) - Remove the configured OpenTelemetry Prometheus metrics path flag +- [thv config otel unset-endpoint](thv_config_otel_unset-endpoint.md) - Remove the configured OpenTelemetry endpoint +- [thv config otel unset-env-vars](thv_config_otel_unset-env-vars.md) - Remove the configured OpenTelemetry environment variables +- [thv config otel unset-insecure](thv_config_otel_unset-insecure.md) - Remove the configured OpenTelemetry insecure transport flag +- [thv config otel unset-metrics-enabled](thv_config_otel_unset-metrics-enabled.md) - Remove the configured OpenTelemetry metrics export flag +- [thv config otel unset-sampling-rate](thv_config_otel_unset-sampling-rate.md) - Remove the configured OpenTelemetry sampling rate +- [thv config otel unset-tracing-enabled](thv_config_otel_unset-tracing-enabled.md) - Remove the configured OpenTelemetry tracing export flag diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_get-enable-prometheus-metrics-path.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_get-enable-prometheus-metrics-path.md new file mode 100644 index 00000000..39c763fc --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_get-enable-prometheus-metrics-path.md @@ -0,0 +1,38 @@ +--- +title: thv config otel get-enable-prometheus-metrics-path +hide_title: true +description: Reference for ToolHive CLI command `thv config otel get-enable-prometheus-metrics-path` +last_update: + author: autogenerated +slug: thv_config_otel_get-enable-prometheus-metrics-path +mdx: + format: md +--- + +## thv config otel get-enable-prometheus-metrics-path + +Get the currently configured OpenTelemetry Prometheus metrics path flag + +### Synopsis + +Display the OpenTelemetry Prometheus metrics path flag that is currently configured. + +``` +thv config otel get-enable-prometheus-metrics-path [flags] +``` + +### Options + +``` + -h, --help help for get-enable-prometheus-metrics-path +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_get-endpoint.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_get-endpoint.md new file mode 100644 index 00000000..9e5b40e0 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_get-endpoint.md @@ -0,0 +1,38 @@ +--- +title: thv config otel get-endpoint +hide_title: true +description: Reference for ToolHive CLI command `thv config otel get-endpoint` +last_update: + author: autogenerated +slug: thv_config_otel_get-endpoint +mdx: + format: md +--- + +## thv config otel get-endpoint + +Get the currently configured OpenTelemetry endpoint + +### Synopsis + +Display the OpenTelemetry endpoint URL that is currently configured. + +``` +thv config otel get-endpoint [flags] +``` + +### Options + +``` + -h, --help help for get-endpoint +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_get-env-vars.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_get-env-vars.md new file mode 100644 index 00000000..1763e558 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_get-env-vars.md @@ -0,0 +1,38 @@ +--- +title: thv config otel get-env-vars +hide_title: true +description: Reference for ToolHive CLI command `thv config otel get-env-vars` +last_update: + author: autogenerated +slug: thv_config_otel_get-env-vars +mdx: + format: md +--- + +## thv config otel get-env-vars + +Get the currently configured OpenTelemetry environment variables + +### Synopsis + +Display the OpenTelemetry environment variables that are currently configured. + +``` +thv config otel get-env-vars [flags] +``` + +### Options + +``` + -h, --help help for get-env-vars +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_get-insecure.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_get-insecure.md new file mode 100644 index 00000000..2445c685 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_get-insecure.md @@ -0,0 +1,38 @@ +--- +title: thv config otel get-insecure +hide_title: true +description: Reference for ToolHive CLI command `thv config otel get-insecure` +last_update: + author: autogenerated +slug: thv_config_otel_get-insecure +mdx: + format: md +--- + +## thv config otel get-insecure + +Get the currently configured OpenTelemetry insecure transport flag + +### Synopsis + +Display the OpenTelemetry insecure transport flag that is currently configured. + +``` +thv config otel get-insecure [flags] +``` + +### Options + +``` + -h, --help help for get-insecure +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_get-metrics-enabled.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_get-metrics-enabled.md new file mode 100644 index 00000000..a0f9bdb7 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_get-metrics-enabled.md @@ -0,0 +1,38 @@ +--- +title: thv config otel get-metrics-enabled +hide_title: true +description: Reference for ToolHive CLI command `thv config otel get-metrics-enabled` +last_update: + author: autogenerated +slug: thv_config_otel_get-metrics-enabled +mdx: + format: md +--- + +## thv config otel get-metrics-enabled + +Get the currently configured OpenTelemetry metrics export flag + +### Synopsis + +Display the OpenTelemetry metrics export flag that is currently configured. + +``` +thv config otel get-metrics-enabled [flags] +``` + +### Options + +``` + -h, --help help for get-metrics-enabled +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_get-sampling-rate.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_get-sampling-rate.md new file mode 100644 index 00000000..c83bcb85 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_get-sampling-rate.md @@ -0,0 +1,38 @@ +--- +title: thv config otel get-sampling-rate +hide_title: true +description: Reference for ToolHive CLI command `thv config otel get-sampling-rate` +last_update: + author: autogenerated +slug: thv_config_otel_get-sampling-rate +mdx: + format: md +--- + +## thv config otel get-sampling-rate + +Get the currently configured OpenTelemetry sampling rate + +### Synopsis + +Display the OpenTelemetry sampling rate that is currently configured. + +``` +thv config otel get-sampling-rate [flags] +``` + +### Options + +``` + -h, --help help for get-sampling-rate +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_get-tracing-enabled.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_get-tracing-enabled.md new file mode 100644 index 00000000..7b267175 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_get-tracing-enabled.md @@ -0,0 +1,38 @@ +--- +title: thv config otel get-tracing-enabled +hide_title: true +description: Reference for ToolHive CLI command `thv config otel get-tracing-enabled` +last_update: + author: autogenerated +slug: thv_config_otel_get-tracing-enabled +mdx: + format: md +--- + +## thv config otel get-tracing-enabled + +Get the currently configured OpenTelemetry tracing export flag + +### Synopsis + +Display the OpenTelemetry tracing export flag that is currently configured. + +``` +thv config otel get-tracing-enabled [flags] +``` + +### Options + +``` + -h, --help help for get-tracing-enabled +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_set-enable-prometheus-metrics-path.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_set-enable-prometheus-metrics-path.md new file mode 100644 index 00000000..ac86ed05 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_set-enable-prometheus-metrics-path.md @@ -0,0 +1,40 @@ +--- +title: thv config otel set-enable-prometheus-metrics-path +hide_title: true +description: Reference for ToolHive CLI command `thv config otel set-enable-prometheus-metrics-path` +last_update: + author: autogenerated +slug: thv_config_otel_set-enable-prometheus-metrics-path +mdx: + format: md +--- + +## thv config otel set-enable-prometheus-metrics-path + +Set the OpenTelemetry Prometheus metrics path flag + +### Synopsis + +Set the OpenTelemetry Prometheus metrics path flag to enable /metrics endpoint. + + thv config otel set-enable-prometheus-metrics-path true + +``` +thv config otel set-enable-prometheus-metrics-path [flags] +``` + +### Options + +``` + -h, --help help for set-enable-prometheus-metrics-path +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_set-endpoint.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_set-endpoint.md new file mode 100644 index 00000000..b98b2fbf --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_set-endpoint.md @@ -0,0 +1,44 @@ +--- +title: thv config otel set-endpoint +hide_title: true +description: Reference for ToolHive CLI command `thv config otel set-endpoint` +last_update: + author: autogenerated +slug: thv_config_otel_set-endpoint +mdx: + format: md +--- + +## thv config otel set-endpoint + +Set the OpenTelemetry endpoint URL + +### Synopsis + +Set the OpenTelemetry OTLP endpoint URL for tracing and metrics. + +This endpoint will be used by default when running MCP servers unless overridden by the --otel-endpoint flag. + +Example: + + thv config otel set-endpoint https://api.honeycomb.io + +``` +thv config otel set-endpoint [flags] +``` + +### Options + +``` + -h, --help help for set-endpoint +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_set-env-vars.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_set-env-vars.md new file mode 100644 index 00000000..7ec87b4d --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_set-env-vars.md @@ -0,0 +1,44 @@ +--- +title: thv config otel set-env-vars +hide_title: true +description: Reference for ToolHive CLI command `thv config otel set-env-vars` +last_update: + author: autogenerated +slug: thv_config_otel_set-env-vars +mdx: + format: md +--- + +## thv config otel set-env-vars + +Set the OpenTelemetry environment variables + +### Synopsis + +Set the list of environment variable names to include in OpenTelemetry spans. + +These environment variables will be used by default when running MCP servers unless overridden by the --otel-env-vars flag. + +Example: + + thv config otel set-env-vars USER,HOME,PATH + +``` +thv config otel set-env-vars [flags] +``` + +### Options + +``` + -h, --help help for set-env-vars +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_set-insecure.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_set-insecure.md new file mode 100644 index 00000000..da645ac1 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_set-insecure.md @@ -0,0 +1,40 @@ +--- +title: thv config otel set-insecure +hide_title: true +description: Reference for ToolHive CLI command `thv config otel set-insecure` +last_update: + author: autogenerated +slug: thv_config_otel_set-insecure +mdx: + format: md +--- + +## thv config otel set-insecure + +Set the OpenTelemetry insecure transport flag + +### Synopsis + +Set the OpenTelemetry insecure flag to enable HTTP instead of HTTPS for OTLP endpoints. + + thv config otel set-insecure true + +``` +thv config otel set-insecure [flags] +``` + +### Options + +``` + -h, --help help for set-insecure +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_set-metrics-enabled.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_set-metrics-enabled.md new file mode 100644 index 00000000..30ffddea --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_set-metrics-enabled.md @@ -0,0 +1,40 @@ +--- +title: thv config otel set-metrics-enabled +hide_title: true +description: Reference for ToolHive CLI command `thv config otel set-metrics-enabled` +last_update: + author: autogenerated +slug: thv_config_otel_set-metrics-enabled +mdx: + format: md +--- + +## thv config otel set-metrics-enabled + +Set the OpenTelemetry metrics export to enabled + +### Synopsis + +Set the OpenTelemetry metrics flag to enable to export metrics to an OTel collector. + + thv config otel set-metrics-enabled true + +``` +thv config otel set-metrics-enabled [flags] +``` + +### Options + +``` + -h, --help help for set-metrics-enabled +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_set-sampling-rate.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_set-sampling-rate.md new file mode 100644 index 00000000..212d42a0 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_set-sampling-rate.md @@ -0,0 +1,44 @@ +--- +title: thv config otel set-sampling-rate +hide_title: true +description: Reference for ToolHive CLI command `thv config otel set-sampling-rate` +last_update: + author: autogenerated +slug: thv_config_otel_set-sampling-rate +mdx: + format: md +--- + +## thv config otel set-sampling-rate + +Set the OpenTelemetry sampling rate + +### Synopsis + +Set the OpenTelemetry trace sampling rate (between 0.0 and 1.0). + +This sampling rate will be used by default when running MCP servers unless overridden by the --otel-sampling-rate flag. + +Example: + + thv config otel set-sampling-rate 0.1 + +``` +thv config otel set-sampling-rate [flags] +``` + +### Options + +``` + -h, --help help for set-sampling-rate +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_set-tracing-enabled.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_set-tracing-enabled.md new file mode 100644 index 00000000..03692e58 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_set-tracing-enabled.md @@ -0,0 +1,40 @@ +--- +title: thv config otel set-tracing-enabled +hide_title: true +description: Reference for ToolHive CLI command `thv config otel set-tracing-enabled` +last_update: + author: autogenerated +slug: thv_config_otel_set-tracing-enabled +mdx: + format: md +--- + +## thv config otel set-tracing-enabled + +Set the OpenTelemetry tracing export to enabled + +### Synopsis + +Set the OpenTelemetry tracing flag to enable to export traces to an OTel collector. + + thv config otel set-tracing-enabled true + +``` +thv config otel set-tracing-enabled [flags] +``` + +### Options + +``` + -h, --help help for set-tracing-enabled +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_unset-enable-prometheus-metrics-path.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_unset-enable-prometheus-metrics-path.md new file mode 100644 index 00000000..4abb8663 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_unset-enable-prometheus-metrics-path.md @@ -0,0 +1,38 @@ +--- +title: thv config otel unset-enable-prometheus-metrics-path +hide_title: true +description: Reference for ToolHive CLI command `thv config otel unset-enable-prometheus-metrics-path` +last_update: + author: autogenerated +slug: thv_config_otel_unset-enable-prometheus-metrics-path +mdx: + format: md +--- + +## thv config otel unset-enable-prometheus-metrics-path + +Remove the configured OpenTelemetry Prometheus metrics path flag + +### Synopsis + +Remove the OpenTelemetry Prometheus metrics path flag configuration. + +``` +thv config otel unset-enable-prometheus-metrics-path [flags] +``` + +### Options + +``` + -h, --help help for unset-enable-prometheus-metrics-path +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_unset-endpoint.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_unset-endpoint.md new file mode 100644 index 00000000..7d58e48f --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_unset-endpoint.md @@ -0,0 +1,38 @@ +--- +title: thv config otel unset-endpoint +hide_title: true +description: Reference for ToolHive CLI command `thv config otel unset-endpoint` +last_update: + author: autogenerated +slug: thv_config_otel_unset-endpoint +mdx: + format: md +--- + +## thv config otel unset-endpoint + +Remove the configured OpenTelemetry endpoint + +### Synopsis + +Remove the OpenTelemetry endpoint configuration. + +``` +thv config otel unset-endpoint [flags] +``` + +### Options + +``` + -h, --help help for unset-endpoint +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_unset-env-vars.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_unset-env-vars.md new file mode 100644 index 00000000..20839e51 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_unset-env-vars.md @@ -0,0 +1,38 @@ +--- +title: thv config otel unset-env-vars +hide_title: true +description: Reference for ToolHive CLI command `thv config otel unset-env-vars` +last_update: + author: autogenerated +slug: thv_config_otel_unset-env-vars +mdx: + format: md +--- + +## thv config otel unset-env-vars + +Remove the configured OpenTelemetry environment variables + +### Synopsis + +Remove the OpenTelemetry environment variables configuration. + +``` +thv config otel unset-env-vars [flags] +``` + +### Options + +``` + -h, --help help for unset-env-vars +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_unset-insecure.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_unset-insecure.md new file mode 100644 index 00000000..15b4f4e5 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_unset-insecure.md @@ -0,0 +1,38 @@ +--- +title: thv config otel unset-insecure +hide_title: true +description: Reference for ToolHive CLI command `thv config otel unset-insecure` +last_update: + author: autogenerated +slug: thv_config_otel_unset-insecure +mdx: + format: md +--- + +## thv config otel unset-insecure + +Remove the configured OpenTelemetry insecure transport flag + +### Synopsis + +Remove the OpenTelemetry insecure transport flag configuration. + +``` +thv config otel unset-insecure [flags] +``` + +### Options + +``` + -h, --help help for unset-insecure +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_unset-metrics-enabled.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_unset-metrics-enabled.md new file mode 100644 index 00000000..af9271e5 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_unset-metrics-enabled.md @@ -0,0 +1,38 @@ +--- +title: thv config otel unset-metrics-enabled +hide_title: true +description: Reference for ToolHive CLI command `thv config otel unset-metrics-enabled` +last_update: + author: autogenerated +slug: thv_config_otel_unset-metrics-enabled +mdx: + format: md +--- + +## thv config otel unset-metrics-enabled + +Remove the configured OpenTelemetry metrics export flag + +### Synopsis + +Remove the OpenTelemetry metrics export flag configuration. + +``` +thv config otel unset-metrics-enabled [flags] +``` + +### Options + +``` + -h, --help help for unset-metrics-enabled +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_unset-sampling-rate.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_unset-sampling-rate.md new file mode 100644 index 00000000..199b9eb0 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_unset-sampling-rate.md @@ -0,0 +1,38 @@ +--- +title: thv config otel unset-sampling-rate +hide_title: true +description: Reference for ToolHive CLI command `thv config otel unset-sampling-rate` +last_update: + author: autogenerated +slug: thv_config_otel_unset-sampling-rate +mdx: + format: md +--- + +## thv config otel unset-sampling-rate + +Remove the configured OpenTelemetry sampling rate + +### Synopsis + +Remove the OpenTelemetry sampling rate configuration. + +``` +thv config otel unset-sampling-rate [flags] +``` + +### Options + +``` + -h, --help help for unset-sampling-rate +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_unset-tracing-enabled.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_unset-tracing-enabled.md new file mode 100644 index 00000000..dbbcfc00 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_otel_unset-tracing-enabled.md @@ -0,0 +1,38 @@ +--- +title: thv config otel unset-tracing-enabled +hide_title: true +description: Reference for ToolHive CLI command `thv config otel unset-tracing-enabled` +last_update: + author: autogenerated +slug: thv_config_otel_unset-tracing-enabled +mdx: + format: md +--- + +## thv config otel unset-tracing-enabled + +Remove the configured OpenTelemetry tracing export flag + +### Synopsis + +Remove the OpenTelemetry tracing export flag configuration. + +``` +thv config otel unset-tracing-enabled [flags] +``` + +### Options + +``` + -h, --help help for unset-tracing-enabled +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_set-build-auth-file.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_set-build-auth-file.md new file mode 100644 index 00000000..04751d2d --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_set-build-auth-file.md @@ -0,0 +1,66 @@ +--- +title: thv config set-build-auth-file +hide_title: true +description: Reference for ToolHive CLI command `thv config set-build-auth-file` +last_update: + author: autogenerated +slug: thv_config_set-build-auth-file +mdx: + format: md +--- + +## thv config set-build-auth-file + +Set an auth file for protocol builds + +### Synopsis + +Set authentication file content that will be injected into the container +during protocol builds (npx://, uvx://, go://). This is useful for authenticating +to private package registries. + +Supported file types: +npmrc - NPM configuration (~/.npmrc) for npm/npx registries +netrc - Netrc file (~/.netrc) for pip, Go, and other tools +yarnrc - Yarn configuration (~/.yarnrc) + +The file content is injected into the build stage only and is NOT included +in the final container image. + +Examples: + +# Set npmrc for private npm registry + +thv config set-build-auth-file npmrc '//npm.corp.example.com/:\_authToken=TOKEN' + +# Set netrc for pip/Go authentication + +thv config set-build-auth-file netrc 'machine github.com login git password TOKEN' + +# Read content from stdin (avoids exposing secrets in shell history) + +cat ~/.npmrc | thv config set-build-auth-file npmrc --stdin +thv config set-build-auth-file npmrc --stdin < ~/.npmrc + +Note: For multi-line content, use quotes, heredoc syntax, or --stdin. + +``` +thv config set-build-auth-file [content] [flags] +``` + +### Options + +``` + -h, --help help for set-build-auth-file + --stdin Read file content from stdin instead of command line argument (default false) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_set-build-env.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_set-build-env.md new file mode 100644 index 00000000..485102e7 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_set-build-env.md @@ -0,0 +1,67 @@ +--- +title: thv config set-build-env +hide_title: true +description: Reference for ToolHive CLI command `thv config set-build-env` +last_update: + author: autogenerated +slug: thv_config_set-build-env +mdx: + format: md +--- + +## thv config set-build-env + +Set a build environment variable for protocol builds + +### Synopsis + +Set a build environment variable that will be injected into Dockerfiles +during protocol builds (npx://, uvx://, go://). This is useful for configuring +custom package mirrors in corporate environments. + +Environment variable names must: + +- Start with an uppercase letter +- Contain only uppercase letters, numbers, and underscores +- Not be a reserved system variable (PATH, HOME, etc.) + +You can set the value in three ways: + +1. Directly: thv config set-build-env KEY value +2. From a ToolHive secret: thv config set-build-env KEY --from-secret secret-name +3. From shell environment: thv config set-build-env KEY --from-env + +Common use cases: + +- NPM_CONFIG_REGISTRY: Custom npm registry URL +- PIP_INDEX_URL: Custom PyPI index URL +- UV_DEFAULT_INDEX: Custom uv package index URL +- GOPROXY: Custom Go module proxy URL +- GOPRIVATE: Private Go module paths + +Examples: +thv config set-build-env NPM_CONFIG_REGISTRY https://npm.corp.example.com +thv config set-build-env GITHUB_TOKEN --from-secret github-pat +thv config set-build-env ARTIFACTORY_API_KEY --from-env + +``` +thv config set-build-env [value] [flags] +``` + +### Options + +``` + --from-env Read value from shell environment at build time + --from-secret Read value from a ToolHive secret at build time (value argument becomes secret name) + -h, --help help for set-build-env +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_set-ca-cert.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_set-ca-cert.md new file mode 100644 index 00000000..48c9fbda --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_set-ca-cert.md @@ -0,0 +1,42 @@ +--- +title: thv config set-ca-cert +hide_title: true +description: Reference for ToolHive CLI command `thv config set-ca-cert` +last_update: + author: autogenerated +slug: thv_config_set-ca-cert +mdx: + format: md +--- + +## thv config set-ca-cert + +Set the default CA certificate for container builds + +### Synopsis + +Set the default CA certificate file path that will be used for all container builds. +This is useful in corporate environments with TLS inspection where custom CA certificates are required. + +Example: +thv config set-ca-cert /path/to/corporate-ca.crt + +``` +thv config set-ca-cert [flags] +``` + +### Options + +``` + -h, --help help for set-ca-cert +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_set-registry-auth.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_set-registry-auth.md new file mode 100644 index 00000000..5941544e --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_set-registry-auth.md @@ -0,0 +1,51 @@ +--- +title: thv config set-registry-auth +hide_title: true +description: Reference for ToolHive CLI command `thv config set-registry-auth` +last_update: + author: autogenerated +slug: thv_config_set-registry-auth +mdx: + format: md +--- + +## thv config set-registry-auth + +Configure OAuth/OIDC authentication for the registry + +### Synopsis + +Configure OAuth/OIDC authentication for the remote MCP server registry. +PKCE (S256) is always enforced for security. + +The issuer URL is validated via OIDC discovery before saving. + +Examples: +thv config set-registry-auth --issuer https://auth.company.com --client-id toolhive-cli +thv config set-registry-auth \ + --issuer https://auth.company.com --client-id toolhive-cli \ + --audience api://my-registry --scopes openid,profile + +``` +thv config set-registry-auth [flags] +``` + +### Options + +``` + --audience string OAuth audience parameter + --client-id string OAuth client ID (required) + -h, --help help for set-registry-auth + --issuer string OIDC issuer URL (required) + --scopes strings OAuth scopes (default [openid,offline_access]) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_set-registry.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_set-registry.md new file mode 100644 index 00000000..8ea55c72 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_set-registry.md @@ -0,0 +1,50 @@ +--- +title: thv config set-registry +hide_title: true +description: Reference for ToolHive CLI command `thv config set-registry` +last_update: + author: autogenerated +slug: thv_config_set-registry +mdx: + format: md +--- + +## thv config set-registry + +Set the MCP server registry + +### Synopsis + +Set the MCP server registry to a remote URL, local file path, or API endpoint. +The command automatically detects the registry type: + +- URLs ending with .json are treated as static registry files +- Other URLs are treated as MCP Registry API endpoints (v0.1 spec) +- Local paths are treated as local registry files + +Examples: +thv config set-registry https://example.com/registry.json # Static remote file +thv config set-registry https://registry.example.com # API endpoint +thv config set-registry /path/to/local-registry.json # Local file path +thv config set-registry file:///path/to/local-registry.json # Explicit file URL + +``` +thv config set-registry [flags] +``` + +### Options + +``` + -p, --allow-private-ip Allow setting the registry URL or API endpoint, even if it references a private IP address (default false) + -h, --help help for set-registry +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_unset-build-auth-file.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_unset-build-auth-file.md new file mode 100644 index 00000000..7e3264d8 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_unset-build-auth-file.md @@ -0,0 +1,43 @@ +--- +title: thv config unset-build-auth-file +hide_title: true +description: Reference for ToolHive CLI command `thv config unset-build-auth-file` +last_update: + author: autogenerated +slug: thv_config_unset-build-auth-file +mdx: + format: md +--- + +## thv config unset-build-auth-file + +Remove build auth file(s) + +### Synopsis + +Remove a specific build auth file or all files. + +Examples: +thv config unset-build-auth-file npmrc # Remove specific file +thv config unset-build-auth-file --all # Remove all files + +``` +thv config unset-build-auth-file [name] [flags] +``` + +### Options + +``` + --all Remove all build auth files + -h, --help help for unset-build-auth-file +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_unset-build-env.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_unset-build-env.md new file mode 100644 index 00000000..a6addeaa --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_unset-build-env.md @@ -0,0 +1,43 @@ +--- +title: thv config unset-build-env +hide_title: true +description: Reference for ToolHive CLI command `thv config unset-build-env` +last_update: + author: autogenerated +slug: thv_config_unset-build-env +mdx: + format: md +--- + +## thv config unset-build-env + +Remove build environment variable(s) + +### Synopsis + +Remove a specific build environment variable or all variables. + +Examples: +thv config unset-build-env NPM_CONFIG_REGISTRY # Remove specific variable +thv config unset-build-env --all # Remove all variables + +``` +thv config unset-build-env [KEY] [flags] +``` + +### Options + +``` + --all Remove all build environment variables + -h, --help help for unset-build-env +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_unset-ca-cert.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_unset-ca-cert.md new file mode 100644 index 00000000..b6fb8992 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_unset-ca-cert.md @@ -0,0 +1,38 @@ +--- +title: thv config unset-ca-cert +hide_title: true +description: Reference for ToolHive CLI command `thv config unset-ca-cert` +last_update: + author: autogenerated +slug: thv_config_unset-ca-cert +mdx: + format: md +--- + +## thv config unset-ca-cert + +Remove the configured CA certificate + +### Synopsis + +Remove the CA certificate configuration, reverting to default behavior without custom CA certificates. + +``` +thv config unset-ca-cert [flags] +``` + +### Options + +``` + -h, --help help for unset-ca-cert +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_unset-registry-auth.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_unset-registry-auth.md new file mode 100644 index 00000000..d1b70b03 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_unset-registry-auth.md @@ -0,0 +1,38 @@ +--- +title: thv config unset-registry-auth +hide_title: true +description: Reference for ToolHive CLI command `thv config unset-registry-auth` +last_update: + author: autogenerated +slug: thv_config_unset-registry-auth +mdx: + format: md +--- + +## thv config unset-registry-auth + +Remove registry authentication configuration + +### Synopsis + +Remove the OAuth/OIDC authentication configuration for the registry. + +``` +thv config unset-registry-auth [flags] +``` + +### Options + +``` + -h, --help help for unset-registry-auth +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_unset-registry.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_unset-registry.md new file mode 100644 index 00000000..00688b5a --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_unset-registry.md @@ -0,0 +1,38 @@ +--- +title: thv config unset-registry +hide_title: true +description: Reference for ToolHive CLI command `thv config unset-registry` +last_update: + author: autogenerated +slug: thv_config_unset-registry +mdx: + format: md +--- + +## thv config unset-registry + +Remove the configured registry + +### Synopsis + +Remove the registry configuration, reverting to the built-in registry. + +``` +thv config unset-registry [flags] +``` + +### Options + +``` + -h, --help help for unset-registry +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_usage-metrics.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_usage-metrics.md new file mode 100644 index 00000000..5ed0914f --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_config_usage-metrics.md @@ -0,0 +1,34 @@ +--- +title: thv config usage-metrics +hide_title: true +description: Reference for ToolHive CLI command `thv config usage-metrics` +last_update: + author: autogenerated +slug: thv_config_usage-metrics +mdx: + format: md +--- + +## thv config usage-metrics + +Enable or disable anonymous usage metrics + +``` +thv config usage-metrics [flags] +``` + +### Options + +``` + -h, --help help for usage-metrics +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_export.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_export.md new file mode 100644 index 00000000..74638285 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_export.md @@ -0,0 +1,58 @@ +--- +title: thv export +hide_title: true +description: Reference for ToolHive CLI command `thv export` +last_update: + author: autogenerated +slug: thv_export +mdx: + format: md +--- + +## thv export + +Export a workload's run configuration to a file + +### Synopsis + +Export a workload's run configuration to a file for sharing or backup. + +The exported configuration can be used with 'thv run --from-config ' to recreate +the same workload with identical settings. + +You can export in different formats: + +- json: Export as RunConfig JSON (default, can be used with 'thv run --from-config') +- k8s: Export as Kubernetes MCPServer resource YAML + +Examples: + + # Export a workload configuration to a JSON file + thv export my-server ./my-server-config.json + + # Export as Kubernetes MCPServer resource + thv export my-server ./my-server.yaml --format k8s + + # Export to a specific directory + thv export github-mcp /tmp/configs/github-config.json + +``` +thv export [flags] +``` + +### Options + +``` + --format string Export format: json or k8s (default "json") + -h, --help help for export +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_group.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_group.md new file mode 100644 index 00000000..0ec6fedb --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_group.md @@ -0,0 +1,38 @@ +--- +title: thv group +hide_title: true +description: Reference for ToolHive CLI command `thv group` +last_update: + author: autogenerated +slug: thv_group +mdx: + format: md +--- + +## thv group + +Manage logical groupings of MCP servers + +### Synopsis + +The group command provides subcommands to manage logical groupings of MCP servers. + +### Options + +``` + -h, --help help for group +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers +- [thv group create](thv_group_create.md) - Create a new group of MCP servers +- [thv group list](thv_group_list.md) - List all groups +- [thv group rm](thv_group_rm.md) - Remove a group and remove workloads from it +- [thv group run](thv_group_run.md) - Deploy all MCP servers from a registry group diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_group_create.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_group_create.md new file mode 100644 index 00000000..72b6a873 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_group_create.md @@ -0,0 +1,39 @@ +--- +title: thv group create +hide_title: true +description: Reference for ToolHive CLI command `thv group create` +last_update: + author: autogenerated +slug: thv_group_create +mdx: + format: md +--- + +## thv group create + +Create a new group of MCP servers + +### Synopsis + +Create a new logical group of MCP servers. +The group can be used to organize and manage multiple MCP servers together. + +``` +thv group create [group-name] [flags] +``` + +### Options + +``` + -h, --help help for create +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv group](thv_group.md) - Manage logical groupings of MCP servers diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_group_list.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_group_list.md new file mode 100644 index 00000000..4e7dbb66 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_group_list.md @@ -0,0 +1,38 @@ +--- +title: thv group list +hide_title: true +description: Reference for ToolHive CLI command `thv group list` +last_update: + author: autogenerated +slug: thv_group_list +mdx: + format: md +--- + +## thv group list + +List all groups + +### Synopsis + +List all logical groups of MCP servers. + +``` +thv group list [flags] +``` + +### Options + +``` + -h, --help help for list +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv group](thv_group.md) - Manage logical groupings of MCP servers diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_group_rm.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_group_rm.md new file mode 100644 index 00000000..6405bc2a --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_group_rm.md @@ -0,0 +1,39 @@ +--- +title: thv group rm +hide_title: true +description: Reference for ToolHive CLI command `thv group rm` +last_update: + author: autogenerated +slug: thv_group_rm +mdx: + format: md +--- + +## thv group rm + +Remove a group and remove workloads from it + +### Synopsis + +Remove a group and remove all MCP servers from it. By default, this only removes the group membership from workloads without deleting them. Use --with-workloads to also delete the workloads. + +``` +thv group rm [group-name] [flags] +``` + +### Options + +``` + -h, --help help for rm + --with-workloads Delete all workloads in the group along with the group (default false) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv group](thv_group.md) - Manage logical groupings of MCP servers diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_group_run.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_group_run.md new file mode 100644 index 00000000..264e225e --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_group_run.md @@ -0,0 +1,41 @@ +--- +title: thv group run +hide_title: true +description: Reference for ToolHive CLI command `thv group run` +last_update: + author: autogenerated +slug: thv_group_run +mdx: + format: md +--- + +## thv group run + +Deploy all MCP servers from a registry group + +### Synopsis + +Deploy all MCP servers defined in a registry group. +This creates a new runtime group and starts all MCP servers within it. + +``` +thv group run [group-name] [flags] +``` + +### Options + +``` + --env stringArray Environment variables to pass to an MCP server in the group (format: SERVER_NAME.KEY=VALUE) + -h, --help help for run + --secret stringArray Secrets to be fetched from the secrets manager and set as environment variables (format: NAME,target=SERVER_NAME.TARGET) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv group](thv_group.md) - Manage logical groupings of MCP servers diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_inspector.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_inspector.md new file mode 100644 index 00000000..cf16cf0e --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_inspector.md @@ -0,0 +1,40 @@ +--- +title: thv inspector +hide_title: true +description: Reference for ToolHive CLI command `thv inspector` +last_update: + author: autogenerated +slug: thv_inspector +mdx: + format: md +--- + +## thv inspector + +Launches the MCP Inspector UI and connects it to the specified MCP server + +### Synopsis + +Launches the MCP Inspector UI and connects it to the specified MCP server + +``` +thv inspector [workload-name] [flags] +``` + +### Options + +``` + -h, --help help for inspector + -p, --mcp-proxy-port int Port to run the MCP Proxy on (default 6277) + -u, --ui-port int Port to run the MCP Inspector UI on (default 6274) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_list.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_list.md new file mode 100644 index 00000000..abe24057 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_list.md @@ -0,0 +1,64 @@ +--- +title: thv list +hide_title: true +description: Reference for ToolHive CLI command `thv list` +last_update: + author: autogenerated +slug: thv_list +mdx: + format: md +--- + +## thv list + +List running MCP servers + +### Synopsis + +List all MCP servers managed by ToolHive, including their status and configuration. + +Examples: + +# List running MCP servers + +thv list + +# List all MCP servers (including stopped) + +thv list --all + +# List servers in JSON format + +thv list --format json + +# List servers in a specific group + +thv list --group production + +# List servers with specific labels + +thv list --label env=dev --label team=backend + +``` +thv list [flags] +``` + +### Options + +``` + -a, --all Show all workloads (default shows just running) + --format string Output format (json, text, mcpservers) (default "text") + --group string Filter by group + -h, --help help for list + -l, --label stringArray Filter workloads by labels (format: key=value) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_logs.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_logs.md new file mode 100644 index 00000000..63d563a1 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_logs.md @@ -0,0 +1,62 @@ +--- +title: thv logs +hide_title: true +description: Reference for ToolHive CLI command `thv logs` +last_update: + author: autogenerated +slug: thv_logs +mdx: + format: md +--- + +## thv logs + +Output the logs of an MCP server or manage log files + +### Synopsis + +Output the logs of an MCP server managed by ToolHive, or manage log files. + +By default, this command shows the logs from the MCP server container. +Use --proxy to view the logs from the ToolHive proxy process instead. + +Examples: + +# View logs of an MCP server + +thv logs filesystem + +# Follow logs in real-time + +thv logs filesystem --follow + +# View proxy logs instead of container logs + +thv logs filesystem --proxy + +# Clean up old log files + +thv logs prune + +``` +thv logs [workload-name|prune] [flags] +``` + +### Options + +``` + -f, --follow Follow log output (only for workload logs) (default false) + -h, --help help for logs + -p, --proxy Show proxy logs instead of container logs (default false) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers +- [thv logs prune](thv_logs_prune.md) - Delete log files from servers not currently managed by ToolHive diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_logs_prune.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_logs_prune.md new file mode 100644 index 00000000..bd7ca809 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_logs_prune.md @@ -0,0 +1,39 @@ +--- +title: thv logs prune +hide_title: true +description: Reference for ToolHive CLI command `thv logs prune` +last_update: + author: autogenerated +slug: thv_logs_prune +mdx: + format: md +--- + +## thv logs prune + +Delete log files from servers not currently managed by ToolHive + +### Synopsis + +Delete log files from servers that are not currently managed by ToolHive (running or stopped). +This helps clean up old log files that accumulate over time from removed servers. + +``` +thv logs prune [flags] +``` + +### Options + +``` + -h, --help help for prune +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv logs](thv_logs.md) - Output the logs of an MCP server or manage log files diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_mcp.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_mcp.md new file mode 100644 index 00000000..4a3d91f0 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_mcp.md @@ -0,0 +1,36 @@ +--- +title: thv mcp +hide_title: true +description: Reference for ToolHive CLI command `thv mcp` +last_update: + author: autogenerated +slug: thv_mcp +mdx: + format: md +--- + +## thv mcp + +Interact with MCP servers for debugging + +### Synopsis + +The mcp command provides subcommands to interact with MCP (Model Context Protocol) servers for debugging purposes. + +### Options + +``` + -h, --help help for mcp +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers +- [thv mcp list](thv_mcp_list.md) - List MCP server capabilities +- [thv mcp serve](thv_mcp_serve.md) - 🧪 EXPERIMENTAL: Start an MCP server to control ToolHive diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_mcp_list.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_mcp_list.md new file mode 100644 index 00000000..51139e47 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_mcp_list.md @@ -0,0 +1,45 @@ +--- +title: thv mcp list +hide_title: true +description: Reference for ToolHive CLI command `thv mcp list` +last_update: + author: autogenerated +slug: thv_mcp_list +mdx: + format: md +--- + +## thv mcp list + +List MCP server capabilities + +### Synopsis + +List tools, resources, and prompts available from an MCP server. Use subcommands to list specific types. + +``` +thv mcp list [tools|resources|prompts] [flags] +``` + +### Options + +``` + --format string Output format (json, text) (default "text") + -h, --help help for list + --server string MCP server URL or name from ToolHive registry (required) + --timeout duration Connection timeout (default 30s) + --transport string Transport type (auto, sse, streamable-http) (default "auto") +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv mcp](thv_mcp.md) - Interact with MCP servers for debugging +- [thv mcp list prompts](thv_mcp_list_prompts.md) - List available prompts from MCP server +- [thv mcp list resources](thv_mcp_list_resources.md) - List available resources from MCP server +- [thv mcp list tools](thv_mcp_list_tools.md) - List available tools from MCP server diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_mcp_list_prompts.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_mcp_list_prompts.md new file mode 100644 index 00000000..b6a2a844 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_mcp_list_prompts.md @@ -0,0 +1,42 @@ +--- +title: thv mcp list prompts +hide_title: true +description: Reference for ToolHive CLI command `thv mcp list prompts` +last_update: + author: autogenerated +slug: thv_mcp_list_prompts +mdx: + format: md +--- + +## thv mcp list prompts + +List available prompts from MCP server + +### Synopsis + +List all prompts available from the specified MCP server. + +``` +thv mcp list prompts [flags] +``` + +### Options + +``` + --format string Output format (json, text) (default "text") + -h, --help help for prompts + --server string MCP server URL or name from ToolHive registry (required) + --timeout duration Connection timeout (default 30s) + --transport string Transport type (auto, sse, streamable-http) (default "auto") +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv mcp list](thv_mcp_list.md) - List MCP server capabilities diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_mcp_list_resources.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_mcp_list_resources.md new file mode 100644 index 00000000..1ee5408e --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_mcp_list_resources.md @@ -0,0 +1,42 @@ +--- +title: thv mcp list resources +hide_title: true +description: Reference for ToolHive CLI command `thv mcp list resources` +last_update: + author: autogenerated +slug: thv_mcp_list_resources +mdx: + format: md +--- + +## thv mcp list resources + +List available resources from MCP server + +### Synopsis + +List all resources available from the specified MCP server. + +``` +thv mcp list resources [flags] +``` + +### Options + +``` + --format string Output format (json, text) (default "text") + -h, --help help for resources + --server string MCP server URL or name from ToolHive registry (required) + --timeout duration Connection timeout (default 30s) + --transport string Transport type (auto, sse, streamable-http) (default "auto") +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv mcp list](thv_mcp_list.md) - List MCP server capabilities diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_mcp_list_tools.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_mcp_list_tools.md new file mode 100644 index 00000000..166045ed --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_mcp_list_tools.md @@ -0,0 +1,42 @@ +--- +title: thv mcp list tools +hide_title: true +description: Reference for ToolHive CLI command `thv mcp list tools` +last_update: + author: autogenerated +slug: thv_mcp_list_tools +mdx: + format: md +--- + +## thv mcp list tools + +List available tools from MCP server + +### Synopsis + +List all tools available from the specified MCP server. + +``` +thv mcp list tools [flags] +``` + +### Options + +``` + --format string Output format (json, text) (default "text") + -h, --help help for tools + --server string MCP server URL or name from ToolHive registry (required) + --timeout duration Connection timeout (default 30s) + --transport string Transport type (auto, sse, streamable-http) (default "auto") +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv mcp list](thv_mcp_list.md) - List MCP server capabilities diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_mcp_serve.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_mcp_serve.md new file mode 100644 index 00000000..27f27323 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_mcp_serve.md @@ -0,0 +1,44 @@ +--- +title: thv mcp serve +hide_title: true +description: Reference for ToolHive CLI command `thv mcp serve` +last_update: + author: autogenerated +slug: thv_mcp_serve +mdx: + format: md +--- + +## thv mcp serve + +🧪 EXPERIMENTAL: Start an MCP server to control ToolHive + +### Synopsis + +🧪 EXPERIMENTAL: Start an MCP (Model Context Protocol) server that allows external clients to control ToolHive. +The server provides tools to search the registry, run MCP servers, and remove servers. +The server runs in privileged mode and can access the Docker socket directly. + +The port can be configured via the --port flag or the MCP_PORT environment variable. + +``` +thv mcp serve [flags] +``` + +### Options + +``` + -h, --help help for serve + --host string Host to listen on (default "localhost") + --port string Port to listen on (can also be set via MCP_PORT env var) (default "4483") +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv mcp](thv_mcp.md) - Interact with MCP servers for debugging diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_proxy.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_proxy.md new file mode 100644 index 00000000..d3592fff --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_proxy.md @@ -0,0 +1,148 @@ +--- +title: thv proxy +hide_title: true +description: Reference for ToolHive CLI command `thv proxy` +last_update: + author: autogenerated +slug: thv_proxy +mdx: + format: md +--- + +## thv proxy + +Create a transparent proxy for an MCP server with authentication support + +### Synopsis + +Create a transparent HTTP proxy that forwards requests to an MCP server endpoint. + +This command starts a standalone proxy without creating a workload, providing: + +- Transparent request forwarding to the target MCP server +- Optional OAuth/OIDC authentication to remote MCP servers +- Automatic authentication detection via WWW-Authenticate headers +- OIDC-based access control for incoming proxy requests +- Secure credential handling via files or environment variables +- Dynamic client registration (RFC 7591) for automatic OAuth client setup + +#### Authentication modes + +The proxy supports multiple authentication scenarios: + +1. No Authentication: Simple transparent forwarding +2. Outgoing Authentication: Authenticate to remote MCP servers using OAuth/OIDC +3. Incoming Authentication: Protect the proxy endpoint with OIDC validation +4. Bidirectional: Both incoming and outgoing authentication + +#### OAuth client secret sources + +OAuth client secrets can be provided via (in order of precedence): + +1. --remote-auth-client-secret flag (not recommended for production) +2. --remote-auth-client-secret-file flag (secure file-based approach) +3. TOOLHIVE_REMOTE_OAUTH_CLIENT_SECRET environment variable + +#### Dynamic client registration + +When no client credentials are provided, the proxy automatically registers an OAuth client +with the authorization server using RFC 7591 dynamic client registration: + +- No need to pre-configure client ID and secret +- Automatically discovers registration endpoint via OIDC +- Supports PKCE flow for enhanced security + +#### Examples + +Basic transparent proxy: + + thv proxy my-server --target-uri http://localhost:8080 + +Proxy with OIDC authentication to remote server: + + thv proxy my-server --target-uri https://api.example.com \ + --remote-auth --remote-auth-issuer https://auth.example.com \ + --remote-auth-client-id my-client-id \ + --remote-auth-client-secret-file /path/to/secret + +Proxy with non-OIDC OAuth authentication to remote server: + + thv proxy my-server --target-uri https://api.example.com \ + --remote-auth \ + --remote-auth-authorize-url https://auth.example.com/oauth/authorize \ + --remote-auth-token-url https://auth.example.com/oauth/token \ + --remote-auth-client-id my-client-id \ + --remote-auth-client-secret-file /path/to/secret + +Proxy with OIDC protection for incoming requests: + + thv proxy my-server --target-uri http://localhost:8080 \ + --oidc-issuer https://auth.example.com \ + --oidc-audience my-audience + +Auto-detect authentication requirements: + + thv proxy my-server --target-uri https://protected-api.com \ + --remote-auth-client-id my-client-id + +Dynamic client registration (automatic OAuth client setup): + + thv proxy my-server --target-uri https://protected-api.com \ + --remote-auth --remote-auth-issuer https://auth.example.com + +``` +thv proxy [flags] SERVER_NAME +``` + +### Options + +``` + -h, --help help for proxy + --host string Host for the HTTP proxy to listen on (IP or hostname) (default "127.0.0.1") + --oidc-audience string Expected audience for the token + --oidc-client-id string OIDC client ID + --oidc-client-secret string OIDC client secret (optional, for introspection) + --oidc-introspection-url string URL for token introspection endpoint + --oidc-issuer string OIDC issuer URL (e.g., https://accounts.google.com) + --oidc-jwks-url string URL to fetch the JWKS from + --oidc-scopes strings OAuth scopes to advertise in the well-known endpoint (RFC 9728, defaults to 'openid' if not specified) + --port int Port for the HTTP proxy to listen on (host port) + --remote-auth Enable OAuth/OIDC authentication to remote MCP server (default false) + --remote-auth-authorize-url string OAuth authorization endpoint URL (alternative to --remote-auth-issuer for non-OIDC OAuth) + --remote-auth-bearer-token string Bearer token for remote server authentication (alternative to OAuth) + --remote-auth-bearer-token-file string Path to file containing bearer token (alternative to --remote-auth-bearer-token) + --remote-auth-callback-port int Port for OAuth callback server during remote authentication (default 8666) + --remote-auth-client-id string OAuth client ID for remote server authentication (optional if the authorization server supports dynamic client registration (RFC 7591)) + --remote-auth-client-secret string OAuth client secret for remote server authentication (optional if the authorization server supports dynamic client registration (RFC 7591) or if using PKCE) + --remote-auth-client-secret-file string Path to file containing OAuth client secret (alternative to --remote-auth-client-secret) (optional if the authorization server supports dynamic client registration (RFC 7591) or if using PKCE) + --remote-auth-issuer string OAuth/OIDC issuer URL for remote server authentication (e.g., https://accounts.google.com) + --remote-auth-resource string OAuth 2.0 resource indicator (RFC 8707) + --remote-auth-scopes strings OAuth scopes to request for remote server authentication (defaults: OIDC uses 'openid,profile,email') + --remote-auth-skip-browser Skip opening browser for remote server OAuth flow (default false) + --remote-auth-timeout duration Timeout for OAuth authentication flow (e.g., 30s, 1m, 2m30s) (default 30s) + --remote-auth-token-url string OAuth token endpoint URL (alternative to --remote-auth-issuer for non-OIDC OAuth) + --remote-forward-headers stringArray Headers to inject into requests to remote server (format: Name=Value, can be repeated) + --remote-forward-headers-secret stringArray Headers with secret values from ToolHive secrets manager (format: Name=secret-name, can be repeated) + --resource-url string Explicit resource URL for OAuth discovery endpoint (RFC 9728) + --target-uri string URI for the target MCP server (e.g., http://localhost:8080) (required) + --token-exchange-audience string Target audience for exchanged tokens + --token-exchange-client-id string OAuth client ID for token exchange operations + --token-exchange-client-secret string OAuth client secret for token exchange operations + --token-exchange-client-secret-file string Path to file containing OAuth client secret for token exchange (alternative to --token-exchange-client-secret) + --token-exchange-header-name string Custom header name for injecting exchanged token (default: replaces Authorization header) + --token-exchange-scopes strings Scopes to request for exchanged tokens + --token-exchange-subject-token-type string Type of subject token to exchange. Accepts: access_token (default), id_token (required for Google STS) + --token-exchange-url string OAuth 2.0 token exchange endpoint URL (enables token exchange when provided) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers +- [thv proxy stdio](thv_proxy_stdio.md) - Create a stdio-based proxy for an MCP server +- [thv proxy tunnel](thv_proxy_tunnel.md) - Create a tunnel proxy for exposing internal endpoints diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_proxy_stdio.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_proxy_stdio.md new file mode 100644 index 00000000..1ae9df8b --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_proxy_stdio.md @@ -0,0 +1,41 @@ +--- +title: thv proxy stdio +hide_title: true +description: Reference for ToolHive CLI command `thv proxy stdio` +last_update: + author: autogenerated +slug: thv_proxy_stdio +mdx: + format: md +--- + +## thv proxy stdio + +Create a stdio-based proxy for an MCP server + +### Synopsis + +Create a stdio-based proxy that connects stdin/stdout to a target MCP server. + +Example: +thv proxy stdio my-workload + +``` +thv proxy stdio WORKLOAD-NAME [flags] +``` + +### Options + +``` + -h, --help help for stdio +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv proxy](thv_proxy.md) - Create a transparent proxy for an MCP server with authentication support diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_proxy_tunnel.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_proxy_tunnel.md new file mode 100644 index 00000000..1cf94924 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_proxy_tunnel.md @@ -0,0 +1,61 @@ +--- +title: thv proxy tunnel +hide_title: true +description: Reference for ToolHive CLI command `thv proxy tunnel` +last_update: + author: autogenerated +slug: thv_proxy_tunnel +mdx: + format: md +--- + +## thv proxy tunnel + +Create a tunnel proxy for exposing internal endpoints + +### Synopsis + +Create a tunnel proxy for exposing internal endpoints. + + TARGET may be either: + +• a URL (http://..., https://...) -> used directly as the target URI +• a workload name -> resolved to its URL + +Examples: +thv proxy tunnel http://localhost:8080 my-server --tunnel-provider ngrok +thv proxy tunnel my-workload my-server --tunnel-provider ngrok + +Flags: +--tunnel-provider string The provider to use for the tunnel (e.g., "ngrok") - mandatory +--provider-args string JSON object with provider-specific arguments: auth-token (mandatory), +url, pooling, traffic-policy-file +--dry-run If set, only validate the configuration without starting the tunnel + +Examples: +thv proxy tunnel --tunnel-provider ngrok --provider-args '{"auth-token": "your-token", +"url": "https://example.com", "pooling": true}' http://localhost:8080 my-server +thv proxy tunnel --tunnel-provider ngrok --provider-args '{"auth-token": "your-token", +"traffic-policy-file": "/path/to/policy.yml"}' my-workload my-server + +``` +thv proxy tunnel [flags] TARGET SERVER_NAME +``` + +### Options + +``` + -h, --help help for tunnel + --provider-args string JSON object with provider-specific arguments (default "{}") + --tunnel-provider string The provider to use for the tunnel (e.g., 'ngrok') - mandatory +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv proxy](thv_proxy.md) - Create a transparent proxy for an MCP server with authentication support diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_registry.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_registry.md new file mode 100644 index 00000000..eff5f17e --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_registry.md @@ -0,0 +1,38 @@ +--- +title: thv registry +hide_title: true +description: Reference for ToolHive CLI command `thv registry` +last_update: + author: autogenerated +slug: thv_registry +mdx: + format: md +--- + +## thv registry + +Manage MCP server registry + +### Synopsis + +Manage the MCP server registry, including listing and getting information about available MCP servers. + +### Options + +``` + -h, --help help for registry +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers +- [thv registry info](thv_registry_info.md) - Get information about an MCP server +- [thv registry list](thv_registry_list.md) - List available MCP servers +- [thv registry login](thv_registry_login.md) - Authenticate with the configured registry +- [thv registry logout](thv_registry_logout.md) - Clear cached registry credentials diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_registry_info.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_registry_info.md new file mode 100644 index 00000000..3b11e291 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_registry_info.md @@ -0,0 +1,40 @@ +--- +title: thv registry info +hide_title: true +description: Reference for ToolHive CLI command `thv registry info` +last_update: + author: autogenerated +slug: thv_registry_info +mdx: + format: md +--- + +## thv registry info + +Get information about an MCP server + +### Synopsis + +Get detailed information about a specific MCP server in the registry. + +``` +thv registry info [server] [flags] +``` + +### Options + +``` + --format string Output format (json, text) (default "text") + -h, --help help for info + --refresh Force refresh registry cache +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv registry](thv_registry.md) - Manage MCP server registry diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_registry_list.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_registry_list.md new file mode 100644 index 00000000..ad5c708c --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_registry_list.md @@ -0,0 +1,40 @@ +--- +title: thv registry list +hide_title: true +description: Reference for ToolHive CLI command `thv registry list` +last_update: + author: autogenerated +slug: thv_registry_list +mdx: + format: md +--- + +## thv registry list + +List available MCP servers + +### Synopsis + +List all available MCP servers in the registry. + +``` +thv registry list [flags] +``` + +### Options + +``` + --format string Output format (json, text) (default "text") + -h, --help help for list + --refresh Force refresh registry cache +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv registry](thv_registry.md) - Manage MCP server registry diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_registry_login.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_registry_login.md new file mode 100644 index 00000000..a76e0136 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_registry_login.md @@ -0,0 +1,51 @@ +--- +title: thv registry login +hide_title: true +description: Reference for ToolHive CLI command `thv registry login` +last_update: + author: autogenerated +slug: thv_registry_login +mdx: + format: md +--- + +## thv registry login + +Authenticate with the configured registry + +### Synopsis + +Perform an interactive OAuth login against the configured registry. + +If the registry URL or OAuth configuration (issuer, client-id) are not yet +saved in config, you can supply them as flags and they will be persisted +before the login flow begins. + +Examples: +thv registry login +thv registry login --registry https://registry.example.com/api --issuer https://auth.example.com --client-id my-app + +``` +thv registry login [flags] +``` + +### Options + +``` + --audience string OAuth audience (optional) + --client-id string OAuth client ID to save if OAuth is not configured + -h, --help help for login + --issuer string OIDC issuer URL to save if OAuth is not configured + --registry string Registry URL to save if not already configured + --scopes strings OAuth scopes (defaults to openid,offline_access) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv registry](thv_registry.md) - Manage MCP server registry diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_registry_logout.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_registry_logout.md new file mode 100644 index 00000000..cb8e911d --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_registry_logout.md @@ -0,0 +1,38 @@ +--- +title: thv registry logout +hide_title: true +description: Reference for ToolHive CLI command `thv registry logout` +last_update: + author: autogenerated +slug: thv_registry_logout +mdx: + format: md +--- + +## thv registry logout + +Clear cached registry credentials + +### Synopsis + +Remove cached OAuth tokens for the configured registry. + +``` +thv registry logout [flags] +``` + +### Options + +``` + -h, --help help for logout +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv registry](thv_registry.md) - Manage MCP server registry diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_rm.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_rm.md new file mode 100644 index 00000000..f24e9d1c --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_rm.md @@ -0,0 +1,57 @@ +--- +title: thv rm +hide_title: true +description: Reference for ToolHive CLI command `thv rm` +last_update: + author: autogenerated +slug: thv_rm +mdx: + format: md +--- + +## thv rm + +Remove one or more MCP servers + +### Synopsis + +Remove one or more MCP servers managed by ToolHive. +Examples: + +# Remove a single MCP server + +thv rm filesystem + +# Remove multiple MCP servers + +thv rm filesystem github slack + +# Remove all workloads + +thv rm --all + +# Remove all workloads in a group + +thv rm --group production + +``` +thv rm [workload-name...] [flags] +``` + +### Options + +``` + --all Delete all workloads + -g, --group string Filter by group + -h, --help help for rm +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_run.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_run.md new file mode 100644 index 00000000..9b452b40 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_run.md @@ -0,0 +1,212 @@ +--- +title: thv run +hide_title: true +description: Reference for ToolHive CLI command `thv run` +last_update: + author: autogenerated +slug: thv_run +mdx: + format: md +--- + +## thv run + +Run an MCP server + +### Synopsis + +Run an MCP server with the specified name, image, or protocol scheme. + +ToolHive supports five ways to run an MCP server: + +1. From the registry: + + $ thv run server-name [-- args...] + + Looks up the server in the registry and uses its predefined settings + (transport, permissions, environment variables, etc.) + +2. From a container image: + + $ thv run ghcr.io/example/mcp-server:latest [-- args...] + + Runs the specified container image directly with the provided arguments + +3. Using a protocol scheme: + + $ thv run uvx://package-name [-- args...] + $ thv run npx://package-name [-- args...] + $ thv run go://package-name [-- args...] + $ thv run go://./local-path [-- args...] + + Automatically generates a container that runs the specified package + using either uvx (Python with uv package manager), npx (Node.js), + or go (Golang). For Go, you can also specify local paths starting + with './' or '../' to build and run local Go projects. + +4. From an exported configuration: + + $ thv run --from-config + + Runs an MCP server using a previously exported configuration file. + +5. Remote MCP server: + + $ thv run [--name ] + + Runs a remote MCP server as a workload, proxying requests to the specified URL. + This allows remote MCP servers to be managed like local workloads with full + support for client configuration, tool filtering, import/export, etc. + +#### Dynamic client registration + +When no client credentials are provided, ToolHive automatically registers an OAuth client +with the authorization server using RFC 7591 dynamic client registration: + +- No need to pre-configure client ID and secret +- Automatically discovers registration endpoint via OIDC +- Supports PKCE flow for enhanced security + +The container will be started with the specified transport mode and +permission profile. Additional configuration can be provided via flags. + +#### Network Configuration + +You can specify the network mode for the container using the --network flag: + +- Host networking: $ thv run --network host +- Custom network: $ thv run --network my-network +- Default (bridge): $ thv run + +The --network flag accepts any Docker-compatible network mode. + +Examples: + +# Run a server from the registry + +thv run filesystem + +# Run a server with custom arguments and toolsets + +thv run github -- --toolsets repos + +# Run from a container image + +thv run ghcr.io/github/github-mcp-server + +# Run using a protocol scheme (Python with uv) + +thv run uvx://mcp-server-git + +# Run using npx (Node.js) + +thv run npx://@modelcontextprotocol/server-everything + +# Run a server in a specific group + +thv run filesystem --group production + +# Run a remote GitHub MCP server with authentication + +thv run github-remote --remote-auth \ + --remote-auth-client-id \ + --remote-auth-client-secret + +``` +thv run [flags] SERVER_OR_IMAGE_OR_PROTOCOL [-- ARGS...] +``` + +### Options + +``` + --audit-config string Path to the audit configuration file + --authz-config string Path to the authorization configuration file + --ca-cert string Path to a custom CA certificate file to use for container builds + --enable-audit Enable audit logging with default configuration (default false) + --endpoint-prefix string Path prefix to prepend to SSE endpoint URLs (e.g., /playwright) + -e, --env stringArray Environment variables to pass to the MCP server (format: KEY=VALUE) + --env-file string Load environment variables from a single file + --env-file-dir string Load environment variables from all files in a directory + -f, --foreground Run in foreground mode (block until container exits) (default false) + --from-config string Load configuration from exported file + --group string Name of the group this workload should belong to (default "default") + -h, --help help for run + --host string Host for the HTTP proxy to listen on (IP or hostname) (default "127.0.0.1") + --ignore-globally Load global ignore patterns from ~/.config/toolhive/thvignore (default true) + --image-verification string Set image verification mode (warn, enabled, disabled) (default "warn") + --isolate-network Isolate the container network from the host (default false) + --jwks-allow-private-ip Allow JWKS/OIDC endpoints on private IP addresses (use with caution) (default false) + --jwks-auth-token-file string Path to file containing bearer token for authenticating JWKS/OIDC requests + -l, --label stringArray Set labels on the container (format: key=value) + --name string Name of the MCP server (default to auto-generated from image) + --network string Connect the container to a network (e.g., 'host' for host networking) + --oidc-audience string Expected audience for the token + --oidc-client-id string OIDC client ID + --oidc-client-secret string OIDC client secret (optional, for introspection) + --oidc-insecure-allow-http Allow HTTP (non-HTTPS) OIDC issuers for local development/testing (WARNING: Insecure!) (default false) + --oidc-introspection-url string URL for token introspection endpoint + --oidc-issuer string OIDC issuer URL (e.g., https://accounts.google.com) + --oidc-jwks-url string URL to fetch the JWKS from + --oidc-scopes strings OAuth scopes to advertise in the well-known endpoint (RFC 9728, defaults to 'openid' if not specified) + --otel-custom-attributes string Custom resource attributes for OpenTelemetry in key=value format (e.g., server_type=prod,region=us-east-1,team=platform) + --otel-enable-prometheus-metrics-path Enable Prometheus-style /metrics endpoint on the main transport port (default false) + --otel-endpoint string OpenTelemetry OTLP endpoint URL (e.g., https://api.honeycomb.io) + --otel-env-vars stringArray Environment variable names to include in OpenTelemetry spans (comma-separated: ENV1,ENV2) + --otel-headers stringArray OpenTelemetry OTLP headers in key=value format (e.g., x-honeycomb-team=your-api-key) + --otel-insecure Connect to the OpenTelemetry endpoint using HTTP instead of HTTPS (default false) + --otel-metrics-enabled Enable OTLP metrics export (when OTLP endpoint is configured) (default true) + --otel-sampling-rate float OpenTelemetry trace sampling rate (0.0-1.0) (default 0.1) + --otel-service-name string OpenTelemetry service name (defaults to thv-) + --otel-tracing-enabled Enable distributed tracing (when OTLP endpoint is configured) (default true) + --otel-use-legacy-attributes Emit legacy attribute names alongside new OTEL semantic convention names (default true) (default true) + --permission-profile string Permission profile to use (none, network, or path to JSON file) (default is to use the permission profile from the registry or "network" if not part of the registry) + --print-resolved-overlays Debug: show resolved container paths for tmpfs overlays (default false) + --proxy-mode string Proxy mode for stdio (streamable-http or sse (deprecated, will be removed)) (default "streamable-http") + --proxy-port int Port for the HTTP proxy to listen on (host port) + --remote-auth Enable OAuth/OIDC authentication to remote MCP server (default false) + --remote-auth-authorize-url string OAuth authorization endpoint URL (alternative to --remote-auth-issuer for non-OIDC OAuth) + --remote-auth-bearer-token string Bearer token for remote server authentication (alternative to OAuth) + --remote-auth-bearer-token-file string Path to file containing bearer token (alternative to --remote-auth-bearer-token) + --remote-auth-callback-port int Port for OAuth callback server during remote authentication (default 8666) + --remote-auth-client-id string OAuth client ID for remote server authentication (optional if the authorization server supports dynamic client registration (RFC 7591)) + --remote-auth-client-secret string OAuth client secret for remote server authentication (optional if the authorization server supports dynamic client registration (RFC 7591) or if using PKCE) + --remote-auth-client-secret-file string Path to file containing OAuth client secret (alternative to --remote-auth-client-secret) (optional if the authorization server supports dynamic client registration (RFC 7591) or if using PKCE) + --remote-auth-issuer string OAuth/OIDC issuer URL for remote server authentication (e.g., https://accounts.google.com) + --remote-auth-resource string OAuth 2.0 resource indicator (RFC 8707) + --remote-auth-scopes strings OAuth scopes to request for remote server authentication (defaults: OIDC uses 'openid,profile,email') + --remote-auth-skip-browser Skip opening browser for remote server OAuth flow (default false) + --remote-auth-timeout duration Timeout for OAuth authentication flow (e.g., 30s, 1m, 2m30s) (default 30s) + --remote-auth-token-url string OAuth token endpoint URL (alternative to --remote-auth-issuer for non-OIDC OAuth) + --remote-forward-headers stringArray Headers to inject into requests to remote MCP server (format: Name=Value, can be repeated) + --remote-forward-headers-secret stringArray Headers with secret values from ToolHive secrets manager (format: Name=secret-name, can be repeated) + --resource-url string Explicit resource URL for OAuth discovery endpoint (RFC 9728) + --runtime-add-package stringArray Add additional packages to install in the builder stage (can be repeated) + --runtime-image string Override the default base image for protocol schemes (e.g., golang:1.24-alpine, node:20-alpine, python:3.11-slim) + --secret stringArray Specify a secret to be fetched from the secrets manager and set as an environment variable (format: NAME,target=TARGET) + --target-host string Host to forward traffic to (only applicable to SSE or Streamable HTTP transport) (default "127.0.0.1") + --target-port int Port for the container to expose (only applicable to SSE or Streamable HTTP transport) + --thv-ca-bundle string Path to CA certificate bundle for ToolHive HTTP operations (JWKS, OIDC discovery, etc.) + --token-exchange-audience string Target audience for exchanged tokens + --token-exchange-client-id string OAuth client ID for token exchange operations + --token-exchange-client-secret string OAuth client secret for token exchange operations + --token-exchange-client-secret-file string Path to file containing OAuth client secret for token exchange (alternative to --token-exchange-client-secret) + --token-exchange-header-name string Custom header name for injecting exchanged token (default: replaces Authorization header) + --token-exchange-scopes strings Scopes to request for exchanged tokens + --token-exchange-subject-token-type string Type of subject token to exchange. Accepts: access_token (default), id_token (required for Google STS) + --token-exchange-url string OAuth 2.0 token exchange endpoint URL (enables token exchange when provided) + --tools stringArray Filter MCP server tools (comma-separated list of tool names) + --tools-override string Path to a JSON file containing overrides for MCP server tools names and descriptions + --transport string Transport mode (sse, streamable-http or stdio) + --trust-proxy-headers Trust X-Forwarded-* headers from reverse proxies (X-Forwarded-Proto, X-Forwarded-Host, X-Forwarded-Port, X-Forwarded-Prefix) (default false) + -v, --volume stringArray Mount a volume into the container (format: host-path:container-path[:ro]) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_runtime.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_runtime.md new file mode 100644 index 00000000..762b9aa7 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_runtime.md @@ -0,0 +1,31 @@ +--- +title: thv runtime +hide_title: true +description: Reference for ToolHive CLI command `thv runtime` +last_update: + author: autogenerated +slug: thv_runtime +mdx: + format: md +--- + +## thv runtime + +Commands related to the container runtime + +### Options + +``` + -h, --help help for runtime +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers +- [thv runtime check](thv_runtime_check.md) - Ping the container runtime diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_runtime_check.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_runtime_check.md new file mode 100644 index 00000000..32d21b52 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_runtime_check.md @@ -0,0 +1,39 @@ +--- +title: thv runtime check +hide_title: true +description: Reference for ToolHive CLI command `thv runtime check` +last_update: + author: autogenerated +slug: thv_runtime_check +mdx: + format: md +--- + +## thv runtime check + +Ping the container runtime + +### Synopsis + +Ensure the container runtime is responsive. + +``` +thv runtime check [flags] +``` + +### Options + +``` + -h, --help help for check + --timeout int Timeout in seconds for runtime checks (default: 30 seconds) (default 30) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv runtime](thv_runtime.md) - Commands related to the container runtime diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_search.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_search.md new file mode 100644 index 00000000..d965e160 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_search.md @@ -0,0 +1,39 @@ +--- +title: thv search +hide_title: true +description: Reference for ToolHive CLI command `thv search` +last_update: + author: autogenerated +slug: thv_search +mdx: + format: md +--- + +## thv search + +Search for MCP servers + +### Synopsis + +Search for MCP servers in the registry by name, description, or tags. + +``` +thv search [query] [flags] +``` + +### Options + +``` + --format string Output format (json or text) (default "text") + -h, --help help for search +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_secret.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_secret.md new file mode 100644 index 00000000..a6134ae6 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_secret.md @@ -0,0 +1,45 @@ +--- +title: thv secret +hide_title: true +description: Reference for ToolHive CLI command `thv secret` +last_update: + author: autogenerated +slug: thv_secret +mdx: + format: md +--- + +## thv secret + +Manage secrets + +### Synopsis + +Manage secrets using the configured secrets provider. + +The secret command provides subcommands to configure, store, retrieve, and manage secrets securely. + +Run "thv secret setup" first to configure a secrets provider before using any secret operations. + +### Options + +``` + -h, --help help for secret +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers +- [thv secret delete](thv_secret_delete.md) - Delete a secret +- [thv secret get](thv_secret_get.md) - Get a secret +- [thv secret list](thv_secret_list.md) - List all available secrets +- [thv secret provider](thv_secret_provider.md) - Set the secrets provider directly +- [thv secret reset-keyring](thv_secret_reset-keyring.md) - Reset the keyring password +- [thv secret set](thv_secret_set.md) - Set a secret +- [thv secret setup](thv_secret_setup.md) - Set up secrets provider diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_secret_delete.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_secret_delete.md new file mode 100644 index 00000000..fa6285d9 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_secret_delete.md @@ -0,0 +1,44 @@ +--- +title: thv secret delete +hide_title: true +description: Reference for ToolHive CLI command `thv secret delete` +last_update: + author: autogenerated +slug: thv_secret_delete +mdx: + format: md +--- + +## thv secret delete + +Delete a secret + +### Synopsis + +Remove a secret from the configured secrets provider. + +This command permanently deletes the specified secret from your secrets provider. +Once you delete a secret, you cannot recover it unless you have a backup. + +Note that some secrets providers may not support deletion operations. +If your provider is read-only or doesn't support deletion, this command returns an error. + +``` +thv secret delete [flags] +``` + +### Options + +``` + -h, --help help for delete +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv secret](thv_secret.md) - Manage secrets diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_secret_get.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_secret_get.md new file mode 100644 index 00000000..61f0b54e --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_secret_get.md @@ -0,0 +1,44 @@ +--- +title: thv secret get +hide_title: true +description: Reference for ToolHive CLI command `thv secret get` +last_update: + author: autogenerated +slug: thv_secret_get +mdx: + format: md +--- + +## thv secret get + +Get a secret + +### Synopsis + +Retrieve and display the value of a secret by name. + +This command fetches the specified secret from your configured secrets provider +and displays its value. The secret value prints to stdout, making it +suitable for use in scripts or command substitution. + +The secret must exist in your configured secrets provider, otherwise the command returns an error. + +``` +thv secret get [flags] +``` + +### Options + +``` + -h, --help help for get +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv secret](thv_secret.md) - Manage secrets diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_secret_list.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_secret_list.md new file mode 100644 index 00000000..e3df7c0b --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_secret_list.md @@ -0,0 +1,41 @@ +--- +title: thv secret list +hide_title: true +description: Reference for ToolHive CLI command `thv secret list` +last_update: + author: autogenerated +slug: thv_secret_list +mdx: + format: md +--- + +## thv secret list + +List all available secrets + +### Synopsis + +Display all secrets available in the configured secrets provider. + +This command shows the names of all secrets stored in your secrets provider. +If descriptions exist for the secrets, the command displays them alongside the names. + +``` +thv secret list [flags] +``` + +### Options + +``` + -h, --help help for list +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv secret](thv_secret.md) - Manage secrets diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_secret_provider.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_secret_provider.md new file mode 100644 index 00000000..79f525d6 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_secret_provider.md @@ -0,0 +1,48 @@ +--- +title: thv secret provider +hide_title: true +description: Reference for ToolHive CLI command `thv secret provider` +last_update: + author: autogenerated +slug: thv_secret_provider +mdx: + format: md +--- + +## thv secret provider + +Set the secrets provider directly + +### Synopsis + +Configure the secrets provider directly. + +Note: The "thv secret setup" command is recommended for interactive configuration. + +Use this command to set the secrets provider directly without interactive prompts, +making it suitable for scripted deployments and automation. + + Valid secrets providers: + - encrypted: Full read-write secrets provider using AES-256-GCM encryption + - 1password: Read-only secrets provider (requires OP_SERVICE_ACCOUNT_TOKEN) + - environment: Read-only secrets provider from TOOLHIVE_SECRET_* env vars + +``` +thv secret provider [flags] +``` + +### Options + +``` + -h, --help help for provider +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv secret](thv_secret.md) - Manage secrets diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_secret_reset-keyring.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_secret_reset-keyring.md new file mode 100644 index 00000000..09ce133b --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_secret_reset-keyring.md @@ -0,0 +1,53 @@ +--- +title: thv secret reset-keyring +hide_title: true +description: Reference for ToolHive CLI command `thv secret reset-keyring` +last_update: + author: autogenerated +slug: thv_secret_reset-keyring +mdx: + format: md +--- + +## thv secret reset-keyring + +Reset the keyring password + +### Synopsis + +Reset the keyring password used to encrypt secrets. + +This command resets the master password stored in your OS keyring that +encrypts and decrypts secrets when using the 'encrypted' secrets provider. + +Use this command if: + +- You've forgotten your keyring password +- You want to change your encryption password +- Your keyring has become corrupted + +Warning: Resetting the keyring password makes any existing encrypted secrets +inaccessible unless you remember the previous password. You will need to set up +your secrets again after resetting. + +This command only works with the 'encrypted' secrets provider. + +``` +thv secret reset-keyring [flags] +``` + +### Options + +``` + -h, --help help for reset-keyring +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv secret](thv_secret.md) - Manage secrets diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_secret_set.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_secret_set.md new file mode 100644 index 00000000..6264b5db --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_secret_set.md @@ -0,0 +1,60 @@ +--- +title: thv secret set +hide_title: true +description: Reference for ToolHive CLI command `thv secret set` +last_update: + author: autogenerated +slug: thv_secret_set +mdx: + format: md +--- + +## thv secret set + +Set a secret + +### Synopsis + +Create or update a secret with the specified name. + +This command supports two input methods for maximum flexibility: + +Piped input: + +When you pipe data to the command, it reads the secret value from stdin. +Examples: + + $ echo "my-secret-value" | thv secret set my-secret + $ cat secret-file.txt | thv secret set my-secret + +Interactive input: + +When you don't pipe data, the command prompts you to enter the secret value securely. +The input remains hidden for security. +Example: + + $ thv secret set my-secret + Enter secret value (input will be hidden): _ + +The command stores the secret securely using your configured secrets provider. +Note that some providers (like 1Password) are read-only and do not support setting secrets. + +``` +thv secret set [flags] +``` + +### Options + +``` + -h, --help help for set +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv secret](thv_secret.md) - Manage secrets diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_secret_setup.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_secret_setup.md new file mode 100644 index 00000000..55ab8083 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_secret_setup.md @@ -0,0 +1,49 @@ +--- +title: thv secret setup +hide_title: true +description: Reference for ToolHive CLI command `thv secret setup` +last_update: + author: autogenerated +slug: thv_secret_setup +mdx: + format: md +--- + +## thv secret setup + +Set up secrets provider + +### Synopsis + +Interactive setup for configuring a secrets provider. + +This command guides you through selecting and configuring a secrets provider +for storing and retrieving secrets. The setup process validates your +configuration and ensures the selected provider initializes properly. + + Available providers: + - encrypted: Stores secrets in an encrypted file using AES-256-GCM using the OS keyring + - 1password: Read-only access to 1Password secrets (requires OP_SERVICE_ACCOUNT_TOKEN environment variable) + - environment: Read-only access to secrets from TOOLHIVE_SECRET_* env vars + +Run this command before using any other secrets functionality. + +``` +thv secret setup [flags] +``` + +### Options + +``` + -h, --help help for setup +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv secret](thv_secret.md) - Manage secrets diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_serve.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_serve.md new file mode 100644 index 00000000..c7859ea2 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_serve.md @@ -0,0 +1,52 @@ +--- +title: thv serve +hide_title: true +description: Reference for ToolHive CLI command `thv serve` +last_update: + author: autogenerated +slug: thv_serve +mdx: + format: md +--- + +## thv serve + +Start the ToolHive API server + +### Synopsis + +Starts the ToolHive API server and listen for HTTP requests. + +``` +thv serve [flags] +``` + +### Options + +``` + --experimental-mcp EXPERIMENTAL: Enable embedded MCP server for controlling ToolHive + --experimental-mcp-host string EXPERIMENTAL: Host for the embedded MCP server (default "localhost") + --experimental-mcp-port string EXPERIMENTAL: Port for the embedded MCP server (default "4483") + -h, --help help for serve + --host string Host address to bind the server to (default "127.0.0.1") + --oidc-audience string Expected audience for the token + --oidc-client-id string OIDC client ID + --oidc-client-secret string OIDC client secret (optional, for introspection) + --oidc-introspection-url string URL for token introspection endpoint + --oidc-issuer string OIDC issuer URL (e.g., https://accounts.google.com) + --oidc-jwks-url string URL to fetch the JWKS from + --oidc-scopes strings OAuth scopes to advertise in the well-known endpoint (RFC 9728, defaults to 'openid' if not specified) + --openapi Enable OpenAPI documentation endpoints (/api/openapi.json and /api/doc) + --port int Port to bind the server to (default 8080) + --socket string UNIX socket path to bind the server to (overrides host and port if provided) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill.md new file mode 100644 index 00000000..cb30c0d7 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill.md @@ -0,0 +1,42 @@ +--- +title: thv skill +hide_title: true +description: Reference for ToolHive CLI command `thv skill` +last_update: + author: autogenerated +slug: thv_skill +mdx: + format: md +--- + +## thv skill + +Manage skills + +### Synopsis + +The skill command provides subcommands to manage skills. + +### Options + +``` + -h, --help help for skill +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers +- [thv skill build](thv_skill_build.md) - Build a skill +- [thv skill builds](thv_skill_builds.md) - List locally-built skill artifacts +- [thv skill info](thv_skill_info.md) - Show skill details +- [thv skill install](thv_skill_install.md) - Install a skill +- [thv skill list](thv_skill_list.md) - List installed skills +- [thv skill push](thv_skill_push.md) - Push a built skill +- [thv skill uninstall](thv_skill_uninstall.md) - Uninstall a skill +- [thv skill validate](thv_skill_validate.md) - Validate a skill definition diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_build.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_build.md new file mode 100644 index 00000000..434749c2 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_build.md @@ -0,0 +1,41 @@ +--- +title: thv skill build +hide_title: true +description: Reference for ToolHive CLI command `thv skill build` +last_update: + author: autogenerated +slug: thv_skill_build +mdx: + format: md +--- + +## thv skill build + +Build a skill + +### Synopsis + +Build a skill from a local directory into an OCI artifact that can be pushed to a registry. + +On success, prints the OCI reference of the built artifact to stdout. + +``` +thv skill build [path] [flags] +``` + +### Options + +``` + -h, --help help for build + -t, --tag string OCI tag for the built artifact +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv skill](thv_skill.md) - Manage skills diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_builds.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_builds.md new file mode 100644 index 00000000..f82eb841 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_builds.md @@ -0,0 +1,40 @@ +--- +title: thv skill builds +hide_title: true +description: Reference for ToolHive CLI command `thv skill builds` +last_update: + author: autogenerated +slug: thv_skill_builds +mdx: + format: md +--- + +## thv skill builds + +List locally-built skill artifacts + +### Synopsis + +List all locally-built OCI skill artifacts stored in the local OCI store. + +``` +thv skill builds [flags] +``` + +### Options + +``` + --format string Output format (json, text) (default "text") + -h, --help help for builds +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv skill](thv_skill.md) - Manage skills +- [thv skill builds remove](thv_skill_builds_remove.md) - Remove a locally-built skill artifact diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_builds_remove.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_builds_remove.md new file mode 100644 index 00000000..09796324 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_builds_remove.md @@ -0,0 +1,38 @@ +--- +title: thv skill builds remove +hide_title: true +description: Reference for ToolHive CLI command `thv skill builds remove` +last_update: + author: autogenerated +slug: thv_skill_builds_remove +mdx: + format: md +--- + +## thv skill builds remove + +Remove a locally-built skill artifact + +### Synopsis + +Remove a locally-built OCI skill artifact and its blobs from the local OCI store. + +``` +thv skill builds remove [flags] +``` + +### Options + +``` + -h, --help help for remove +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv skill builds](thv_skill_builds.md) - List locally-built skill artifacts diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_info.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_info.md new file mode 100644 index 00000000..5a08f317 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_info.md @@ -0,0 +1,41 @@ +--- +title: thv skill info +hide_title: true +description: Reference for ToolHive CLI command `thv skill info` +last_update: + author: autogenerated +slug: thv_skill_info +mdx: + format: md +--- + +## thv skill info + +Show skill details + +### Synopsis + +Display detailed information about a skill, including metadata, version, and installation status. + +``` +thv skill info [skill-name] [flags] +``` + +### Options + +``` + --format string Output format (json, text) (default "text") + -h, --help help for info + --project-root string Project root path for project-scoped skills + --scope string Filter by scope (user, project) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv skill](thv_skill.md) - Manage skills diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_install.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_install.md new file mode 100644 index 00000000..9bad3969 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_install.md @@ -0,0 +1,44 @@ +--- +title: thv skill install +hide_title: true +description: Reference for ToolHive CLI command `thv skill install` +last_update: + author: autogenerated +slug: thv_skill_install +mdx: + format: md +--- + +## thv skill install + +Install a skill + +### Synopsis + +Install a skill by name or OCI reference. +The skill will be fetched from a remote registry and installed locally. + +``` +thv skill install [skill-name] [flags] +``` + +### Options + +``` + --clients string Comma-separated target client apps (e.g. claude-code,opencode), or "all" for every available client + --force Overwrite existing skill directory + --group string Group to add the skill to after installation + -h, --help help for install + --project-root string Project root path for project-scoped installs + --scope string Installation scope (user, project) (default "user") +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv skill](thv_skill.md) - Manage skills diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_list.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_list.md new file mode 100644 index 00000000..62873cc5 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_list.md @@ -0,0 +1,43 @@ +--- +title: thv skill list +hide_title: true +description: Reference for ToolHive CLI command `thv skill list` +last_update: + author: autogenerated +slug: thv_skill_list +mdx: + format: md +--- + +## thv skill list + +List installed skills + +### Synopsis + +List all currently installed skills and their status. + +``` +thv skill list [flags] +``` + +### Options + +``` + --client string Filter by client application + --format string Output format (json, text) (default "text") + --group string Filter by group + -h, --help help for list + --project-root string Project root path for project-scoped skills + --scope string Filter by scope (user, project) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv skill](thv_skill.md) - Manage skills diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_push.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_push.md new file mode 100644 index 00000000..4d3b592e --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_push.md @@ -0,0 +1,38 @@ +--- +title: thv skill push +hide_title: true +description: Reference for ToolHive CLI command `thv skill push` +last_update: + author: autogenerated +slug: thv_skill_push +mdx: + format: md +--- + +## thv skill push + +Push a built skill + +### Synopsis + +Push a previously built skill artifact to a remote OCI registry. + +``` +thv skill push [reference] [flags] +``` + +### Options + +``` + -h, --help help for push +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv skill](thv_skill.md) - Manage skills diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_uninstall.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_uninstall.md new file mode 100644 index 00000000..891c9f07 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_uninstall.md @@ -0,0 +1,40 @@ +--- +title: thv skill uninstall +hide_title: true +description: Reference for ToolHive CLI command `thv skill uninstall` +last_update: + author: autogenerated +slug: thv_skill_uninstall +mdx: + format: md +--- + +## thv skill uninstall + +Uninstall a skill + +### Synopsis + +Remove a previously installed skill by name. + +``` +thv skill uninstall [skill-name] [flags] +``` + +### Options + +``` + -h, --help help for uninstall + --project-root string Project root path for project-scoped skills + --scope string Scope to uninstall from (user, project) (default "user") +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv skill](thv_skill.md) - Manage skills diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_validate.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_validate.md new file mode 100644 index 00000000..43b92ec6 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_skill_validate.md @@ -0,0 +1,39 @@ +--- +title: thv skill validate +hide_title: true +description: Reference for ToolHive CLI command `thv skill validate` +last_update: + author: autogenerated +slug: thv_skill_validate +mdx: + format: md +--- + +## thv skill validate + +Validate a skill definition + +### Synopsis + +Check that a skill definition in the given directory is valid and well-formed. + +``` +thv skill validate [path] [flags] +``` + +### Options + +``` + --format string Output format (json, text) (default "text") + -h, --help help for validate +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv skill](thv_skill.md) - Manage skills diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_start.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_start.md new file mode 100644 index 00000000..46b0423c --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_start.md @@ -0,0 +1,44 @@ +--- +title: thv start +hide_title: true +description: Reference for ToolHive CLI command `thv start` +last_update: + author: autogenerated +slug: thv_start +mdx: + format: md +--- + +## thv start + +Start (resume) a tooling server + +### Synopsis + +Start (or resume) a tooling server managed by ToolHive. +If the server is not running, it will be started. +The alias "thv restart" is kept for backward compatibility. +Supports both container-based and remote MCP servers. + +``` +thv start [workload-name] [flags] +``` + +### Options + +``` + -a, --all Restart all MCP servers + -f, --foreground Run the restarted workload in foreground mode (default false) + -g, --group string Filter by group + -h, --help help for start +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_status.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_status.md new file mode 100644 index 00000000..62528a4a --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_status.md @@ -0,0 +1,39 @@ +--- +title: thv status +hide_title: true +description: Reference for ToolHive CLI command `thv status` +last_update: + author: autogenerated +slug: thv_status +mdx: + format: md +--- + +## thv status + +Show detailed status of an MCP server + +### Synopsis + +Display detailed status information for a specific MCP server managed by ToolHive. + +``` +thv status [workload-name] [flags] +``` + +### Options + +``` + --format string Output format (json or text) (default "text") + -h, --help help for status +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_stop.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_stop.md new file mode 100644 index 00000000..f128a3d6 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_stop.md @@ -0,0 +1,57 @@ +--- +title: thv stop +hide_title: true +description: Reference for ToolHive CLI command `thv stop` +last_update: + author: autogenerated +slug: thv_stop +mdx: + format: md +--- + +## thv stop + +Stop one or more MCP servers + +### Synopsis + +Stop one or more running MCP servers managed by ToolHive. Examples: + +# Stop a single MCP server + +thv stop filesystem + +# Stop multiple MCP servers + +thv stop filesystem github slack + +# Stop all running MCP servers + +thv stop --all + +# Stop all servers in a group + +thv stop --group production + +``` +thv stop [workload-name...] [flags] +``` + +### Options + +``` + -a, --all Stop all running MCP servers + -g, --group string Filter by group + -h, --help help for stop + --timeout int Timeout in seconds before forcibly stopping the workload (default 30) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers diff --git a/versioned_docs/version-1.0/toolhive/reference/cli/thv_version.md b/versioned_docs/version-1.0/toolhive/reference/cli/thv_version.md new file mode 100644 index 00000000..8ec32468 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/cli/thv_version.md @@ -0,0 +1,40 @@ +--- +title: thv version +hide_title: true +description: Reference for ToolHive CLI command `thv version` +last_update: + author: autogenerated +slug: thv_version +mdx: + format: md +--- + +## thv version + +Show the version of ToolHive + +### Synopsis + +Display detailed version information about ToolHive, including version number, git commit, build date, and Go version. + +``` +thv version [flags] +``` + +### Options + +``` + --format string Output format (json or text) (default "text") + -h, --help help for version + --json Output version information as JSON (deprecated, use --format instead) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers diff --git a/versioned_docs/version-1.0/toolhive/reference/client-compatibility.mdx b/versioned_docs/version-1.0/toolhive/reference/client-compatibility.mdx new file mode 100644 index 00000000..b93d2b66 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/client-compatibility.mdx @@ -0,0 +1,375 @@ +--- +title: Client compatibility +description: Overview of supported AI clients and their compatibility with ToolHive. +--- + +This page shows which AI clients work with ToolHive and how they connect to MCP +servers. + +## Supported clients + +We've tested ToolHive with these clients: + +| Client | Supported | Auto-configuration | Notes | +| -------------------------- | :-------: | :----------------: | ------------------------------------------- | +| GitHub Copilot (VS Code) | ✅ | ✅ | v1.102+ or Insiders version ([see note][3]) | +| Claude Code | ✅ | ✅ | v1.0.27+ | +| Cursor | ✅ | ✅ | v0.50.0+ | +| Google Antigravity | ✅ | ✅ | | +| Gemini CLI | ✅ | ✅ | | +| Cline (VS Code) | ✅ | ✅ | v3.17.10+ | +| Continue (VS Code) | ✅ | ✅ | v1.0.14+ | +| Continue (JetBrains) | ✅ | ✅ | v1.0.23+ | +| Goose | ✅ | ✅ | | +| Kiro | ✅ | ✅ | v0.5.0+ | +| LM Studio | ✅ | ✅ | v0.3.17+ | +| Mistral Vibe | ✅ | ✅ | | +| OpenAI Codex | ✅ | ✅ | | +| OpenCode | ✅ | ✅ | | +| Roo Code (VS Code) | ✅ | ✅ | v3.19.2+ | +| Sourcegraph Amp CLI | ✅ | ✅ | | +| Sourcegraph Amp (VS Code) | ✅ | ✅ | | +| Sourcegraph Amp (Cursor) | ✅ | ✅ | | +| Sourcegraph Amp (Windsurf) | ✅ | ✅ | | +| Trae IDE | ✅ | ✅ | v1.4.1+ | +| VS Code Server | ✅ | ✅ | | +| Windsurf IDE | ✅ | ✅ | | +| Windsurf (JetBrains) | ✅ | ✅ | | +| Zed | ✅ | ✅ | v0.214.5+ | +| ChatGPT Desktop | ✅ | ❌ | See [STDIO-only client configuration][4] | +| Claude Desktop | ✅ | ❌ | See [STDIO-only client configuration][4] | +| GitHub Copilot (JetBrains) | ✅ | ❌ | v1.5.47+ | +| PydanticAI | ✅ | ❌ | v0.2.18+ | + +[3]: #vs-code-with-copilot +[4]: #stdio-only-client-configuration + +The minimum versions listed are the earliest versions that support the +Streamable HTTP transport protocol. + +You can also use other clients and development libraries that support the SSE +and/or Streamable HTTP protocol, but you'll need to configure them manually. + +## Client requirements + +To work with ToolHive, your client needs to: + +1. Support the Model Context Protocol (MCP) +2. Connect to MCP servers using the server-sent events (SSE) or Streamable HTTP + transport protocol +3. Have the correct MCP server URL configured + +## Automatic configuration support + +ToolHive can automatically configure supported clients to connect to MCP +servers. See the +[UI client configuration](../guides-ui/client-configuration.mdx) or +[CLI client configuration guide](../guides-cli/client-configuration.mdx) for +more details. + +Check the table above to see which clients support automatic configuration. + +For other clients, you'll need to set up the MCP server URL yourself. + +## Configuration locations + +ToolHive manages client configurations in their standard locations. + +### VS Code with Copilot + +GitHub Copilot in VS Code stores its +[global MCP configuration](https://code.visualstudio.com/docs/copilot/chat/mcp-servers#_add-an-mcp-server-to-your-user-settings) +in your VS Code user settings file. + +:::note + +VS Code versions prior to v1.102 stored MCP server settings in the main +`settings.json` file. As of v1.102, this has moved to a dedicated `mcp.json` +file. + +ToolHive v0.2.0 and works with VS Code 1.102+ and updates the `mcp.json` file +when you run an MCP server. + +If you're using an older version of either ToolHive or VS Code, automatic client +configuration will not work as expected and you will need to manually update +your configurations. We recommend upgrading both for the best experience. + +::: + +**Standard version**: + +- **macOS**: `~/Library/Application Support/Code/User/mcp.json` +- **Linux**: `~/.config/Code/User/mcp.json` + +**Insiders edition**: + +- **macOS**: `~/Library/Application Support/Code - Insiders/User/mcp.json` +- **Linux**: `~/.config/Code - Insiders/User/mcp.json` + +Example configuration: + +```json +{ + "servers": { + "github": { "url": "http://localhost:19046/mcp", "type": "http" }, + "fetch": { "url": "http://localhost:43832/mcp", "type": "http" }, + "sqlite": { "url": "http://localhost:51712/sse#sqlite", "type": "sse" } + } +} +``` + +When you register VS Code as a client, ToolHive automatically updates the global +MCP configuration file whenever you run an MCP server. You can also +[configure project-specific MCP servers](https://code.visualstudio.com/docs/copilot/chat/mcp-servers#_add-an-mcp-server-to-your-workspace) +by creating a `.vscode/mcp.json` file in your project directory. + +### VS Code Server + +[VS Code Server](https://code.visualstudio.com/docs/remote/vscode-server) is the +server-side component that enables remote development with Visual Studio Code. +It stores its global MCP configuration in a JSON file on the remote server. + +- **All platforms**: `~/.vscode-server/data/User/mcp.json` + +Example configuration: + +```json +{ + "servers": { + "github": { "url": "http://localhost:19046/mcp", "type": "http" }, + "fetch": { "url": "http://localhost:43832/mcp", "type": "http" }, + "sqlite": { "url": "http://localhost:51712/sse#sqlite", "type": "sse" } + } +} +``` + +When you register VS Code Server as a client, ToolHive automatically updates the +global MCP configuration file whenever you run an MCP server. + +### Cursor + +[Cursor](https://cursor.so/) stores its global MCP configuration in a JSON file +in your home directory. + +- **All platforms**: `~/.cursor/mcp.json` + +Example configuration: + +```json +{ + "mcpServers": { + "github": { "url": "http://localhost:19046/mcp" }, + "fetch": { "url": "http://localhost:43832/mcp" }, + "sqlite": { "url": "http://localhost:51712/sse#sqlite" } + } +} +``` + +Cursor automatically detects the transport mode for HTTP MCP servers. + +When you register Cursor as a client, ToolHive automatically updates the global +MCP configuration file whenever you run an MCP server. You can also +[configure project-specific MCP servers](https://docs.cursor.com/context/model-context-protocol#configuration-locations) +by creating a `.cursor/mcp.json` file in your project directory. + +### Claude Code + +[Claude Code](https://www.anthropic.com/claude-code) stores its global (user +scope) MCP configuration in a JSON file in your home directory. + +- **All platforms**: `~/.claude.json` + +Example configuration: + +```json +{ + // Other Claude Code settings... + + "mcpServers": { + "github": { "url": "http://localhost:19046/mcp", "type": "http" }, + "fetch": { "url": "http://localhost:43832/mcp", "type": "http" }, + "sqlite": { "url": "http://localhost:51712/sse#sqlite", "type": "sse" } + } +} +``` + +When you register Claude Code as a client, ToolHive automatically updates the +global MCP configuration file whenever you run an MCP server. You can also +[configure project-specific MCP servers](https://docs.anthropic.com/en/docs/claude-code/mcp#understanding-mcp-server-scopes) +by creating a `.mcp.json` file in your project directory, or add servers using +the `claude` CLI: + +```bash +claude mcp add --scope --transport http fetch http://localhost:43832/mcp +``` + +### Roo Code and Cline + +[Roo Code](https://roocode.com/) (previously Roo Cline) and +[Cline](https://cline.bot/) store their global MCP configuration in their VS +Code extension settings directory. Both use the same configuration format. + +**Roo Code**: + +- **macOS**: + `~/Library/Application Support/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/mcp_settings.json` +- **Linux**: + `~/.config/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/mcp_settings.json` + +**Cline**: + +- **macOS**: + `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json` +- **Linux**: + `~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json` + +Example configuration: + +```json +{ + "mcpServers": { + "github": { + "url": "http://localhost:19046/mcp", + "type": "streamable-http" + }, + "fetch": { "url": "http://localhost:43832/mcp", "type": "streamable-http" }, + "sqlite": { "url": "http://localhost:51712/sse#sqlite", "type": "sse" } + // Note: Cline uses "streamableHttp" instead of "streamable-http" + } +} +``` + +When you register Roo Code or Cline as a client, ToolHive automatically updates +the global MCP configuration file whenever you run an MCP server. With Roo Code, +you can also configure +[project-specific MCP servers](https://docs.roocode.com/features/mcp/using-mcp-in-roo#editing-mcp-settings-files) +by creating a `.roo/mcp.json` file in your project directory. + +### Continue + +The [Continue](https://continue.dev) extension stores its global MCP +configuration in a JSON file in your home directory. This file is used by both +the VS Code and JetBrains versions of Continue. + +- **All platforms**: `~/.continue/config.yaml` + +Example configuration: + +```yaml +mcpServers: + - name: github + type: streamable-http + url: http://localhost:19046/mcp + - name: fetch + type: streamable-http + url: http://localhost:43832/mcp + - name: sqlite + type: sse + url: http://localhost:51712/sse#sqlite +``` + +Continue supports SSE (`type: sse`) and Streamable HTTP +(`type: streamable-http`) transports. + +When you register Continue as a client, ToolHive automatically updates the +global MCP configuration file whenever you run an MCP server. You can also +configure project-specific MCP servers by creating a +`.continue/mcpServers/.yaml` file in your project directory. + +## Manual configuration + +If your client doesn't support automatic configuration, you'll need to set up +the MCP server URL manually. + +### Example: PydanticAI + +For [PydanticAI](https://ai.pydantic.dev/), set the MCP server URL in your code: + +```python +from pydantic_ai.mcp import MCPServerSSE +from pydantic_ai.mcp import MCPServerStreamableHTTP + +# For Streamable HTTP +server = MCPServerStreamableHTTP(url='http://localhost:43832/mcp') + +# For Server-Sent Events (SSE) +server = MCPServerSSE(url='http://localhost:51712/sse#sqlite') +``` + +### Example: Copilot for JetBrains IDEs + +For the +[GitHub Copilot plugin in JetBrains IDEs](https://plugins.jetbrains.com/plugin/17718-github-copilot) +(IntelliJ, Pydantic, etc.), edit your `~/.config/github-copilot/mcp.json` file +to include the MCP server URL: + +```json +{ + "servers": { + "github": { "url": "http://localhost:43832/mcp", "type": "http" }, + "sqlite": { "url": "http://localhost:51712/sse#sqlite", "type": "sse" } + } +} +``` + +Copilot for JetBrains supports SSE (`"type": "sse"`) and Streamable HTTP +(`"type": "streamable-http"`) transports. + +### STDIO-only client configuration + +Some MCP clients, like Claude Desktop, only support MCP servers that communicate +via STDIO transport. However, many popular MCP servers use Server-Sent Events +(SSE) or Streamable HTTP transport protocols. + +ToolHive's `thv proxy stdio` command transforms servers using SSE or Streamable +HTTP into STDIO-compatible processes, enabling these clients to work with any +MCP server. + +**How it works:** + +1. ToolHive runs the MCP server in its container with its default transport + (SSE, HTTP, or other) +2. The `thv proxy stdio` command sets up a STDIO-level wrapper pointing to that + existing server +3. Your client interacts with this wrapper just like any other STDIO MCP server + +**Example:** + +First, run an MCP server with SSE transport: + +```bash +thv run osv --transport sse --name osv +``` + +Then configure your client to use the proxy stdio command. For Claude Desktop, +update your configuration file (typically located at +`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS): + +```json +{ + "mcpServers": { + "osv": { + "command": "thv", + "args": ["proxy", "stdio", "osv"] + } + } +} +``` + +:::tip + +The workload name you pass to `thv proxy stdio` (in this case, `osv`) must match +the name you used when running the server with `thv run`. + +::: + +After restarting your client, the MCP server's tools will be available for use, +even though it uses SSE transport. + +For more details, see the +[`thv proxy stdio` CLI reference](../reference/cli/thv_proxy_stdio.md). + +## Related information + +- [Client configuration](../guides-cli/client-configuration.mdx) +- [Run MCP servers](../guides-cli/run-mcp-servers.mdx) diff --git a/versioned_docs/version-1.0/toolhive/reference/crd-spec.md b/versioned_docs/version-1.0/toolhive/reference/crd-spec.md new file mode 100644 index 00000000..acd272dc --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/crd-spec.md @@ -0,0 +1,2585 @@ +--- +title: Kubernetes CRD reference +sidebar_label: CRD Reference +description: ToolHive Kubernetes Operator Custom Resource Definitions (CRDs) reference. +toc_max_heading_level: 4 +mdx: + format: md +--- + +## Packages + +- [toolhive.stacklok.dev/audit](#toolhivestacklokdevaudit) +- [toolhive.stacklok.dev/authtypes](#toolhivestacklokdevauthtypes) +- [toolhive.stacklok.dev/config](#toolhivestacklokdevconfig) +- [toolhive.stacklok.dev/telemetry](#toolhivestacklokdevtelemetry) +- [toolhive.stacklok.dev/v1alpha1](#toolhivestacklokdevv1alpha1) + +## toolhive.stacklok.dev/audit + +#### pkg.audit.Config + +Config represents the audit logging configuration. + +_Appears in:_ + +- [vmcp.config.Config](#vmcpconfigconfig) + +| Field | Description | Default | Validation | +| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `enabled` _boolean_ | Enabled controls whether audit logging is enabled.
When true, enables audit logging with the configured options. | false | Optional: \{\}
| +| `component` _string_ | Component is the component name to use in audit events. | | Optional: \{\}
| +| `eventTypes` _string array_ | EventTypes specifies which event types to audit. If empty, all events are audited. | | Optional: \{\}
| +| `excludeEventTypes` _string array_ | ExcludeEventTypes specifies which event types to exclude from auditing.
This takes precedence over EventTypes. | | Optional: \{\}
| +| `includeRequestData` _boolean_ | IncludeRequestData determines whether to include request data in audit logs. | false | Optional: \{\}
| +| `includeResponseData` _boolean_ | IncludeResponseData determines whether to include response data in audit logs. | false | Optional: \{\}
| +| `maxDataSize` _integer_ | MaxDataSize limits the size of request/response data included in audit logs (in bytes). | 1024 | Optional: \{\}
| +| `logFile` _string_ | LogFile specifies the file path for audit logs. If empty, logs to stdout. | | Optional: \{\}
| + +## toolhive.stacklok.dev/authtypes + +#### auth.types.BackendAuthStrategy + +BackendAuthStrategy defines how to authenticate to a specific backend. + +This struct provides type-safe configuration for different authentication strategies +using HeaderInjection or TokenExchange fields based on the Type field. + +_Appears in:_ + +- [vmcp.config.OutgoingAuthConfig](#vmcpconfigoutgoingauthconfig) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | ------- | ---------- | +| `type` _string_ | Type is the auth strategy: "unauthenticated", "header_injection", "token_exchange" | | | +| `headerInjection` _[auth.types.HeaderInjectionConfig](#authtypesheaderinjectionconfig)_ | HeaderInjection contains configuration for header injection auth strategy.
Used when Type = "header_injection". | | | +| `tokenExchange` _[auth.types.TokenExchangeConfig](#authtypestokenexchangeconfig)_ | TokenExchange contains configuration for token exchange auth strategy.
Used when Type = "token_exchange". | | | + +#### auth.types.HeaderInjectionConfig + +HeaderInjectionConfig configures the header injection auth strategy. +This strategy injects a static or environment-sourced header value into requests. + +_Appears in:_ + +- [auth.types.BackendAuthStrategy](#authtypesbackendauthstrategy) + +| Field | Description | Default | Validation | +| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------- | +| `headerName` _string_ | HeaderName is the name of the header to inject (e.g., "Authorization"). | | | +| `headerValue` _string_ | HeaderValue is the static header value to inject.
Either HeaderValue or HeaderValueEnv should be set, not both. | | | +| `headerValueEnv` _string_ | HeaderValueEnv is the environment variable name containing the header value.
The value will be resolved at runtime from this environment variable.
Either HeaderValue or HeaderValueEnv should be set, not both. | | | + +#### auth.types.TokenExchangeConfig + +TokenExchangeConfig configures the OAuth 2.0 token exchange auth strategy. +This strategy exchanges incoming tokens for backend-specific tokens using RFC 8693. + +_Appears in:_ + +- [auth.types.BackendAuthStrategy](#authtypesbackendauthstrategy) + +| Field | Description | Default | Validation | +| --------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------- | +| `tokenUrl` _string_ | TokenURL is the OAuth token endpoint URL for token exchange. | | | +| `clientId` _string_ | ClientID is the OAuth client ID for the token exchange request. | | | +| `clientSecret` _string_ | ClientSecret is the OAuth client secret (use ClientSecretEnv for security). | | | +| `clientSecretEnv` _string_ | ClientSecretEnv is the environment variable name containing the client secret.
The value will be resolved at runtime from this environment variable. | | | +| `audience` _string_ | Audience is the target audience for the exchanged token. | | | +| `scopes` _string array_ | Scopes are the requested scopes for the exchanged token. | | | +| `subjectTokenType` _string_ | SubjectTokenType is the token type of the incoming subject token.
Defaults to "urn:ietf:params:oauth:token-type:access_token" if not specified. | | | + +## toolhive.stacklok.dev/config + +#### vmcp.config.AggregationConfig + +AggregationConfig defines tool aggregation, filtering, and conflict resolution strategies. + +Tool Visibility vs Routing: + +- ExcludeAllTools, per-workload ExcludeAll, and Filter control which tools are + advertised to MCP clients (visible in tools/list responses). +- ALL backend tools remain available in the internal routing table, allowing + composite tools to call hidden backend tools. +- This enables curated experiences where raw backend tools are hidden from + MCP clients but accessible through composite tool workflows. + +_Appears in:_ + +- [vmcp.config.Config](#vmcpconfigconfig) + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------- | +| `conflictResolution` _[pkg.vmcp.ConflictResolutionStrategy](#pkgvmcpconflictresolutionstrategy)_ | ConflictResolution defines the strategy for resolving tool name conflicts.
- prefix: Automatically prefix tool names with workload identifier
- priority: First workload in priority order wins
- manual: Explicitly define overrides for all conflicts | prefix | Enum: [prefix priority manual]
Optional: \{\}
| +| `conflictResolutionConfig` _[vmcp.config.ConflictResolutionConfig](#vmcpconfigconflictresolutionconfig)_ | ConflictResolutionConfig provides configuration for the chosen strategy. | | Optional: \{\}
| +| `tools` _[vmcp.config.WorkloadToolConfig](#vmcpconfigworkloadtoolconfig) array_ | Tools defines per-workload tool filtering and overrides. | | Optional: \{\}
| +| `excludeAllTools` _boolean_ | ExcludeAllTools hides all backend tools from MCP clients when true.
Hidden tools are NOT advertised in tools/list responses, but they ARE
available in the routing table for composite tools to use.
This enables the use case where you want to hide raw backend tools from
direct client access while exposing curated composite tool workflows. | | Optional: \{\}
| + +#### vmcp.config.AuthzConfig + +AuthzConfig configures authorization. + +_Appears in:_ + +- [vmcp.config.IncomingAuthConfig](#vmcpconfigincomingauthconfig) + +| Field | Description | Default | Validation | +| ------------------------- | ----------------------------------------------------------------- | ------- | ---------- | +| `type` _string_ | Type is the authz type: "cedar", "none" | | | +| `policies` _string array_ | Policies contains Cedar policy definitions (when Type = "cedar"). | | | + +#### vmcp.config.CircuitBreakerConfig + +CircuitBreakerConfig configures circuit breaker behavior. + +_Appears in:_ + +- [vmcp.config.FailureHandlingConfig](#vmcpconfigfailurehandlingconfig) + +| Field | Description | Default | Validation | +| ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------- | +| `enabled` _boolean_ | Enabled controls whether circuit breaker is enabled. | false | Optional: \{\}
| +| `failureThreshold` _integer_ | FailureThreshold is the number of failures before opening the circuit.
Must be >= 1. | 5 | Minimum: 1
Optional: \{\}
| +| `timeout` _[vmcp.config.Duration](#vmcpconfigduration)_ | Timeout is the duration to wait before attempting to close the circuit.
Must be >= 1s to prevent thrashing. | 60s | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Type: string
Optional: \{\}
| + +#### vmcp.config.CompositeToolConfig + +CompositeToolConfig defines a composite tool workflow. +This matches the YAML structure from the proposal (lines 173-255). + +_Appears in:_ + +- [vmcp.config.Config](#vmcpconfigconfig) +- [api.v1alpha1.VirtualMCPCompositeToolDefinitionSpec](#apiv1alpha1virtualmcpcompositetooldefinitionspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------ | +| `name` _string_ | Name is the workflow name (unique identifier). | | | +| `description` _string_ | Description describes what the workflow does. | | | +| `parameters` _[pkg.json.Map](#pkgjsonmap)_ | Parameters defines input parameter schema in JSON Schema format.
Should be a JSON Schema object with "type": "object" and "properties".
Example:
\{
"type": "object",
"properties": \{
"param1": \{"type": "string", "default": "value"\},
"param2": \{"type": "integer"\}
\},
"required": ["param2"]
\}
We use json.Map rather than a typed struct because JSON Schema is highly
flexible with many optional fields (default, enum, minimum, maximum, pattern,
items, additionalProperties, oneOf, anyOf, allOf, etc.). Using json.Map
allows full JSON Schema compatibility without needing to define every possible
field, and matches how the MCP SDK handles inputSchema. | | Optional: \{\}
| +| `timeout` _[vmcp.config.Duration](#vmcpconfigduration)_ | Timeout is the maximum workflow execution time. | | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Type: string
| +| `steps` _[vmcp.config.WorkflowStepConfig](#vmcpconfigworkflowstepconfig) array_ | Steps are the workflow steps to execute. | | | +| `output` _[vmcp.config.OutputConfig](#vmcpconfigoutputconfig)_ | Output defines the structured output schema for this workflow.
If not specified, the workflow returns the last step's output (backward compatible). | | Optional: \{\}
| + +#### vmcp.config.CompositeToolRef + +CompositeToolRef defines a reference to a VirtualMCPCompositeToolDefinition resource. +The referenced resource must be in the same namespace as the VirtualMCPServer. + +_Appears in:_ + +- [vmcp.config.Config](#vmcpconfigconfig) + +| Field | Description | Default | Validation | +| --------------- | ----------------------------------------------------------------------------------------- | ------- | --------------------- | +| `name` _string_ | Name is the name of the VirtualMCPCompositeToolDefinition resource in the same namespace. | | Required: \{\}
| + +#### vmcp.config.Config + +Config is the unified configuration model for Virtual MCP Server. +This is platform-agnostic and used by both CLI and Kubernetes deployments. + +Platform-specific adapters (CLI YAML loader, Kubernetes CRD converter) +transform their native formats into this model. + +_Validation:_ + +- Type: object + +_Appears in:_ + +- [api.v1alpha1.VirtualMCPServerSpec](#apiv1alpha1virtualmcpserverspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `name` _string_ | Name is the virtual MCP server name. | | Optional: \{\}
| +| `groupRef` _string_ | Group references an existing MCPGroup that defines backend workloads.
In Kubernetes, the referenced MCPGroup must exist in the same namespace. | | Required: \{\}
| +| `backends` _[vmcp.config.StaticBackendConfig](#vmcpconfigstaticbackendconfig) array_ | Backends defines pre-configured backend servers for static mode.
When OutgoingAuth.Source is "inline", this field contains the full list of backend
servers with their URLs and transport types, eliminating the need for K8s API access.
When OutgoingAuth.Source is "discovered", this field is empty and backends are
discovered at runtime via Kubernetes API. | | Optional: \{\}
| +| `incomingAuth` _[vmcp.config.IncomingAuthConfig](#vmcpconfigincomingauthconfig)_ | IncomingAuth configures how clients authenticate to the virtual MCP server.
When using the Kubernetes operator, this is populated by the converter from
VirtualMCPServerSpec.IncomingAuth and any values set here will be superseded. | | Optional: \{\}
| +| `outgoingAuth` _[vmcp.config.OutgoingAuthConfig](#vmcpconfigoutgoingauthconfig)_ | OutgoingAuth configures how the virtual MCP server authenticates to backends.
When using the Kubernetes operator, this is populated by the converter from
VirtualMCPServerSpec.OutgoingAuth and any values set here will be superseded. | | Optional: \{\}
| +| `aggregation` _[vmcp.config.AggregationConfig](#vmcpconfigaggregationconfig)_ | Aggregation defines tool aggregation and conflict resolution strategies.
Supports ToolConfigRef for Kubernetes-native MCPToolConfig resource references. | | Optional: \{\}
| +| `compositeTools` _[vmcp.config.CompositeToolConfig](#vmcpconfigcompositetoolconfig) array_ | CompositeTools defines inline composite tool workflows.
Full workflow definitions are embedded in the configuration.
For Kubernetes, complex workflows can also reference VirtualMCPCompositeToolDefinition CRDs. | | Optional: \{\}
| +| `compositeToolRefs` _[vmcp.config.CompositeToolRef](#vmcpconfigcompositetoolref) array_ | CompositeToolRefs references VirtualMCPCompositeToolDefinition resources
for complex, reusable workflows. Only applicable when running in Kubernetes.
Referenced resources must be in the same namespace as the VirtualMCPServer. | | Optional: \{\}
| +| `operational` _[vmcp.config.OperationalConfig](#vmcpconfigoperationalconfig)_ | Operational configures operational settings. | | | +| `metadata` _object (keys:string, values:string)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `telemetry` _[pkg.telemetry.Config](#pkgtelemetryconfig)_ | Telemetry configures OpenTelemetry-based observability for the Virtual MCP server
including distributed tracing, OTLP metrics export, and Prometheus metrics endpoint. | | Optional: \{\}
| +| `audit` _[pkg.audit.Config](#pkgauditconfig)_ | Audit configures audit logging for the Virtual MCP server.
When present, audit logs include MCP protocol operations.
See audit.Config for available configuration options. | | Optional: \{\}
| +| `optimizer` _[vmcp.config.OptimizerConfig](#vmcpconfigoptimizerconfig)_ | Optimizer configures the MCP optimizer for context optimization on large toolsets.
When enabled, vMCP exposes only find_tool and call_tool operations to clients
instead of all backend tools directly. This reduces token usage by allowing
LLMs to discover relevant tools on demand rather than receiving all tool definitions. | | Optional: \{\}
| + +#### vmcp.config.ConflictResolutionConfig + +ConflictResolutionConfig provides configuration for conflict resolution strategies. + +_Appears in:_ + +- [vmcp.config.AggregationConfig](#vmcpconfigaggregationconfig) + +| Field | Description | Default | Validation | +| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | -------------- | --------------------- | +| `prefixFormat` _string_ | PrefixFormat defines the prefix format for the "prefix" strategy.
Supports placeholders: \{workload\}, \{workload\}\_, \{workload\}. | \{workload\}\_ | Optional: \{\}
| +| `priorityOrder` _string array_ | PriorityOrder defines the workload priority order for the "priority" strategy. | | Optional: \{\}
| + +#### vmcp.config.ElicitationResponseConfig + +ElicitationResponseConfig defines how to handle user responses to elicitation requests. + +_Appears in:_ + +- [vmcp.config.WorkflowStepConfig](#vmcpconfigworkflowstepconfig) + +| Field | Description | Default | Validation | +| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----------------------------------------------------------------- | +| `action` _string_ | Action defines the action to take when the user declines or cancels
- skip_remaining: Skip remaining steps in the workflow
- abort: Abort the entire workflow execution
- continue: Continue to the next step | abort | Enum: [skip_remaining abort continue]
Optional: \{\}
| + +#### vmcp.config.FailureHandlingConfig + +FailureHandlingConfig configures failure handling behavior. + +_Appears in:_ + +- [vmcp.config.OperationalConfig](#vmcpconfigoperationalconfig) + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------- | +| `healthCheckInterval` _[vmcp.config.Duration](#vmcpconfigduration)_ | HealthCheckInterval is the interval between health checks. | 30s | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Type: string
Optional: \{\}
| +| `unhealthyThreshold` _integer_ | UnhealthyThreshold is the number of consecutive failures before marking unhealthy. | 3 | Optional: \{\}
| +| `healthCheckTimeout` _[vmcp.config.Duration](#vmcpconfigduration)_ | HealthCheckTimeout is the maximum duration for a single health check operation.
Should be less than HealthCheckInterval to prevent checks from queuing up. | 10s | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Type: string
Optional: \{\}
| +| `statusReportingInterval` _[vmcp.config.Duration](#vmcpconfigduration)_ | StatusReportingInterval is the interval for reporting status updates to Kubernetes.
This controls how often the vMCP runtime reports backend health and phase changes.
Lower values provide faster status updates but increase API server load. | 30s | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Type: string
Optional: \{\}
| +| `partialFailureMode` _string_ | PartialFailureMode defines behavior when some backends are unavailable.
- fail: Fail entire request if any backend is unavailable
- best_effort: Continue with available backends | fail | Enum: [fail best_effort]
Optional: \{\}
| +| `circuitBreaker` _[vmcp.config.CircuitBreakerConfig](#vmcpconfigcircuitbreakerconfig)_ | CircuitBreaker configures circuit breaker behavior. | | Optional: \{\}
| + +#### vmcp.config.IncomingAuthConfig + +IncomingAuthConfig configures client authentication to the virtual MCP server. + +Note: When using the Kubernetes operator (VirtualMCPServer CRD), the +VirtualMCPServerSpec.IncomingAuth field is the authoritative source for +authentication configuration. The operator's converter will resolve the CRD's +IncomingAuth (which supports Kubernetes-native references like SecretKeyRef, +ConfigMapRef, etc.) and populate this IncomingAuthConfig with the resolved values. +Any values set here directly will be superseded by the CRD configuration. + +_Appears in:_ + +- [vmcp.config.Config](#vmcpconfigconfig) + +| Field | Description | Default | Validation | +| ----------------------------------------------------------- | ------------------------------------------------------ | ------- | ---------- | +| `type` _string_ | Type is the auth type: "oidc", "local", "anonymous" | | | +| `oidc` _[vmcp.config.OIDCConfig](#vmcpconfigoidcconfig)_ | OIDC contains OIDC configuration (when Type = "oidc"). | | | +| `authz` _[vmcp.config.AuthzConfig](#vmcpconfigauthzconfig)_ | Authz contains authorization configuration (optional). | | | + +#### vmcp.config.OIDCConfig + +OIDCConfig configures OpenID Connect authentication. + +_Appears in:_ + +- [vmcp.config.IncomingAuthConfig](#vmcpconfigincomingauthconfig) + +| Field | Description | Default | Validation | +| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------- | +| `issuer` _string_ | Issuer is the OIDC issuer URL. | | Pattern: `^https?://`
| +| `clientId` _string_ | ClientID is the OAuth client ID. | | | +| `clientSecretEnv` _string_ | ClientSecretEnv is the name of the environment variable containing the client secret.
This is the secure way to reference secrets - the actual secret value is never stored
in configuration files, only the environment variable name.
The secret value will be resolved from this environment variable at runtime. | | | +| `audience` _string_ | Audience is the required token audience. | | | +| `resource` _string_ | Resource is the OAuth 2.0 resource indicator (RFC 8707).
Used in WWW-Authenticate header and OAuth discovery metadata (RFC 9728).
If not specified, defaults to Audience. | | | +| `scopes` _string array_ | Scopes are the required OAuth scopes. | | | +| `protectedResourceAllowPrivateIp` _boolean_ | ProtectedResourceAllowPrivateIP allows protected resource endpoint on private IP addresses
Use with caution - only enable for trusted internal IDPs or testing | | | +| `insecureAllowHttp` _boolean_ | InsecureAllowHTTP allows HTTP (non-HTTPS) OIDC issuers for development/testing
WARNING: This is insecure and should NEVER be used in production | | | + +#### vmcp.config.OperationalConfig + +OperationalConfig contains operational settings. +OperationalConfig defines operational settings like timeouts and health checks. + +_Appears in:_ + +- [vmcp.config.Config](#vmcpconfigconfig) + +| Field | Description | Default | Validation | +| ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----------------------------------------- | +| `logLevel` _string_ | LogLevel sets the logging level for the Virtual MCP server.
The only valid value is "debug" to enable debug logging.
When omitted or empty, the server uses info level logging. | | Enum: [debug]
Optional: \{\}
| +| `timeouts` _[vmcp.config.TimeoutConfig](#vmcpconfigtimeoutconfig)_ | Timeouts configures timeout settings. | | Optional: \{\}
| +| `failureHandling` _[vmcp.config.FailureHandlingConfig](#vmcpconfigfailurehandlingconfig)_ | FailureHandling configures failure handling behavior. | | Optional: \{\}
| + +#### vmcp.config.OptimizerConfig + +OptimizerConfig configures the MCP optimizer. +When enabled, vMCP exposes only find_tool and call_tool operations to clients +instead of all backend tools directly. + +_Appears in:_ + +- [vmcp.config.Config](#vmcpconfigconfig) + +| Field | Description | Default | Validation | +| ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------- | +| `embeddingService` _string_ | EmbeddingService is the full base URL of the embedding service endpoint
(e.g., http://my-embedding.default.svc.cluster.local:8080) for semantic
tool discovery.
In a Kubernetes environment, it is more convenient to use the
VirtualMCPServerSpec.EmbeddingServerRef field instead of setting this
directly. EmbeddingServerRef references an EmbeddingServer CRD by name,
and the operator automatically resolves the referenced resource's
Status.URL to populate this field. This provides managed lifecycle
(the operator watches the EmbeddingServer for readiness and URL changes)
and avoids hardcoding service URLs in the config. If both
EmbeddingServerRef and this field are set, EmbeddingServerRef takes
precedence and this value is overridden with a warning. | | Optional: \{\}
| +| `embeddingServiceTimeout` _[vmcp.config.Duration](#vmcpconfigduration)_ | EmbeddingServiceTimeout is the HTTP request timeout for calls to the embedding service.
Defaults to 30s if not specified. | 30s | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Type: string
Optional: \{\}
| +| `maxToolsToReturn` _integer_ | MaxToolsToReturn is the maximum number of tool results returned by a search query.
Defaults to 8 if not specified or zero. | | Maximum: 50
Minimum: 1
Optional: \{\}
| +| `hybridSearchSemanticRatio` _string_ | HybridSearchSemanticRatio controls the balance between semantic (meaning-based)
and keyword search results. 0.0 = all keyword, 1.0 = all semantic.
Defaults to "0.5" if not specified or empty.
Serialized as a string because CRDs do not support float types portably. | | Pattern: `^([0-9]*[.])?[0-9]+$`
Optional: \{\}
| +| `semanticDistanceThreshold` _string_ | SemanticDistanceThreshold is the maximum distance for semantic search results.
Results exceeding this threshold are filtered out from semantic search.
This threshold does not apply to keyword search.
Range: 0 = identical, 2 = completely unrelated.
Defaults to "1.0" if not specified or empty.
Serialized as a string because CRDs do not support float types portably. | | Pattern: `^([0-9]*[.])?[0-9]+$`
Optional: \{\}
| + +#### vmcp.config.OutgoingAuthConfig + +OutgoingAuthConfig configures backend authentication. + +Note: When using the Kubernetes operator (VirtualMCPServer CRD), the +VirtualMCPServerSpec.OutgoingAuth field is the authoritative source for +backend authentication configuration. The operator's converter will resolve +the CRD's OutgoingAuth (which supports Kubernetes-native references like +SecretKeyRef, ConfigMapRef, etc.) and populate this OutgoingAuthConfig with +the resolved values. Any values set here directly will be superseded by the +CRD configuration. + +_Appears in:_ + +- [vmcp.config.Config](#vmcpconfigconfig) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------- | +| `source` _string_ | Source defines how to discover backend auth: "inline", "discovered"
- inline: Explicit configuration in OutgoingAuth
- discovered: Auto-discover from backend MCPServer.externalAuthConfigRef (Kubernetes only) | | | +| `default` _[auth.types.BackendAuthStrategy](#authtypesbackendauthstrategy)_ | Default is the default auth strategy for backends without explicit config. | | | +| `backends` _object (keys:string, values:[auth.types.BackendAuthStrategy](#authtypesbackendauthstrategy))_ | Backends contains per-backend auth configuration. | | | + +#### vmcp.config.OutputConfig + +OutputConfig defines the structured output schema for a composite tool workflow. +This follows the same pattern as the Parameters field, defining both the +MCP output schema (type, description) and runtime value construction (value, default). + +_Appears in:_ + +- [vmcp.config.CompositeToolConfig](#vmcpconfigcompositetoolconfig) +- [api.v1alpha1.VirtualMCPCompositeToolDefinitionSpec](#apiv1alpha1virtualmcpcompositetooldefinitionspec) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `properties` _object (keys:string, values:[vmcp.config.OutputProperty](#vmcpconfigoutputproperty))_ | Properties defines the output properties.
Map key is the property name, value is the property definition. | | | +| `required` _string array_ | Required lists property names that must be present in the output. | | Optional: \{\}
| + +#### vmcp.config.OutputProperty + +OutputProperty defines a single output property. +For non-object types, Value is required. +For object types, either Value or Properties must be specified (but not both). + +_Appears in:_ + +- [vmcp.config.OutputConfig](#vmcpconfigoutputconfig) +- [vmcp.config.OutputProperty](#vmcpconfigoutputproperty) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------ | +| `type` _string_ | Type is the JSON Schema type: "string", "integer", "number", "boolean", "object", "array" | | Enum: [string integer number boolean object array]
Required: \{\}
| +| `description` _string_ | Description is a human-readable description exposed to clients and models | | Optional: \{\}
| +| `value` _string_ | Value is a template string for constructing the runtime value.
For object types, this can be a JSON string that will be deserialized.
Supports template syntax: \{\{.steps.step_id.output.field\}\}, \{\{.params.param_name\}\} | | Optional: \{\}
| +| `properties` _object (keys:string, values:[vmcp.config.OutputProperty](#vmcpconfigoutputproperty))_ | Properties defines nested properties for object types.
Each nested property has full metadata (type, description, value/properties). | | Schemaless: \{\}
Type: object
Optional: \{\}
| +| `default` _[pkg.json.Any](#pkgjsonany)_ | Default is the fallback value if template expansion fails.
Type coercion is applied to match the declared Type. | | Schemaless: \{\}
Optional: \{\}
| + +#### vmcp.config.StaticBackendConfig + +StaticBackendConfig defines a pre-configured backend server for static mode. +This allows vMCP to operate without Kubernetes API access by embedding all backend +information directly in the configuration. + +_Appears in:_ + +- [vmcp.config.Config](#vmcpconfigconfig) + +| Field | Description | Default | Validation | +| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------- | +| `name` _string_ | Name is the backend identifier.
Must match the backend name from the MCPGroup for auth config resolution. | | Required: \{\}
| +| `url` _string_ | URL is the backend's MCP server base URL. | | Pattern: `^https?://`
Required: \{\}
| +| `transport` _string_ | Transport is the MCP transport protocol: "sse" or "streamable-http"
Only network transports supported by vMCP client are allowed. | | Enum: [sse streamable-http]
Required: \{\}
| +| `metadata` _object (keys:string, values:string)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | Optional: \{\}
| + +#### vmcp.config.StepErrorHandling + +StepErrorHandling defines error handling behavior for workflow steps. + +_Appears in:_ + +- [vmcp.config.WorkflowStepConfig](#vmcpconfigworkflowstepconfig) + +| Field | Description | Default | Validation | +| ---------------------------------------------------------- | ------------------------------------------------------------------------------------ | ------- | --------------------------------------------------------------------------------------------------------- | +| `action` _string_ | Action defines the action to take on error | abort | Enum: [abort continue retry]
Optional: \{\}
| +| `retryCount` _integer_ | RetryCount is the maximum number of retries
Only used when Action is "retry" | | Optional: \{\}
| +| `retryDelay` _[vmcp.config.Duration](#vmcpconfigduration)_ | RetryDelay is the delay between retry attempts
Only used when Action is "retry" | | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Type: string
Optional: \{\}
| + +#### vmcp.config.TimeoutConfig + +TimeoutConfig configures timeout settings. + +_Appears in:_ + +- [vmcp.config.OperationalConfig](#vmcpconfigoperationalconfig) + +| Field | Description | Default | Validation | +| ---------------------------------------------------------------------------------------- | ---------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------- | +| `default` _[vmcp.config.Duration](#vmcpconfigduration)_ | Default is the default timeout for backend requests. | 30s | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Type: string
Optional: \{\}
| +| `perWorkload` _object (keys:string, values:[vmcp.config.Duration](#vmcpconfigduration))_ | PerWorkload defines per-workload timeout overrides. | | Optional: \{\}
| + +#### vmcp.config.ToolAnnotationsOverride + +_Underlying type:_ _[vmcp.config.struct{Title *string "json:\"title,omitempty\" yaml:\"title,omitempty\""; ReadOnlyHint *bool "json:\"readOnlyHint,omitempty\" yaml:\"readOnlyHint,omitempty\""; DestructiveHint *bool "json:\"destructiveHint,omitempty\" yaml:\"destructiveHint,omitempty\""; IdempotentHint *bool "json:\"idempotentHint,omitempty\" yaml:\"idempotentHint,omitempty\""; OpenWorldHint *bool "json:\"openWorldHint,omitempty\" yaml:\"openWorldHint,omitempty\""}](#vmcpconfigstruct{title *string "json:\"title,omitempty\" yaml:\"title,omitempty\""; readonlyhint *bool "json:\"readonlyhint,omitempty\" yaml:\"readonlyhint,omitempty\""; destructivehint *bool "json:\"destructivehint,omitempty\" yaml:\"destructivehint,omitempty\""; idempotenthint *bool "json:\"idempotenthint,omitempty\" yaml:\"idempotenthint,omitempty\""; openworldhint \*bool "json:\"openworldhint,omitempty\" yaml:\"openworldhint,omitempty\""})_ + +ToolAnnotationsOverride defines overrides for tool annotation fields. +All fields use pointers so nil means "don't override" while zero values +(empty string, false) mean "explicitly set to this value." + +_Appears in:_ + +- [vmcp.config.ToolOverride](#vmcpconfigtooloverride) + +#### vmcp.config.ToolConfigRef + +ToolConfigRef references an MCPToolConfig resource for tool filtering and renaming. +Only used when running in Kubernetes with the operator. + +_Appears in:_ + +- [vmcp.config.WorkloadToolConfig](#vmcpconfigworkloadtoolconfig) + +| Field | Description | Default | Validation | +| --------------- | --------------------------------------------------------------------- | ------- | --------------------- | +| `name` _string_ | Name is the name of the MCPToolConfig resource in the same namespace. | | Required: \{\}
| + +#### vmcp.config.ToolOverride + +ToolOverride defines tool name, description, and annotation overrides. + +_Appears in:_ + +- [vmcp.config.WorkloadToolConfig](#vmcpconfigworkloadtoolconfig) + +| Field | Description | Default | Validation | +| ----------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `name` _string_ | Name is the new tool name (for renaming). | | Optional: \{\}
| +| `description` _string_ | Description is the new tool description. | | Optional: \{\}
| +| `annotations` _[vmcp.config.ToolAnnotationsOverride](#vmcpconfigtoolannotationsoverride)_ | Annotations overrides specific tool annotation fields.
Only specified fields are overridden; others pass through from the backend. | | Optional: \{\}
| + +#### vmcp.config.WorkflowStepConfig + +WorkflowStepConfig defines a single workflow step. +This matches the proposal's step configuration (lines 180-255). + +_Appears in:_ + +- [vmcp.config.CompositeToolConfig](#vmcpconfigcompositetoolconfig) +- [api.v1alpha1.VirtualMCPCompositeToolDefinitionSpec](#apiv1alpha1virtualmcpcompositetooldefinitionspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | --------------------------------------------------------------------------------------------------------- | +| `id` _string_ | ID is the unique identifier for this step. | | Required: \{\}
| +| `type` _string_ | Type is the step type (tool, elicitation, etc.) | tool | Enum: [tool elicitation]
Optional: \{\}
| +| `tool` _string_ | Tool is the tool to call (format: "workload.tool_name")
Only used when Type is "tool" | | Optional: \{\}
| +| `arguments` _[pkg.json.Map](#pkgjsonmap)_ | Arguments is a map of argument values with template expansion support.
Supports Go template syntax with .params and .steps for string values.
Non-string values (integers, booleans, arrays, objects) are passed as-is.
Note: the templating is only supported on the first level of the key-value pairs. | | Type: object
Optional: \{\}
| +| `condition` _string_ | Condition is a template expression that determines if the step should execute | | Optional: \{\}
| +| `dependsOn` _string array_ | DependsOn lists step IDs that must complete before this step | | Optional: \{\}
| +| `onError` _[vmcp.config.StepErrorHandling](#vmcpconfigsteperrorhandling)_ | OnError defines error handling behavior | | Optional: \{\}
| +| `message` _string_ | Message is the elicitation message
Only used when Type is "elicitation" | | Optional: \{\}
| +| `schema` _[pkg.json.Map](#pkgjsonmap)_ | Schema defines the expected response schema for elicitation | | Type: object
Optional: \{\}
| +| `timeout` _[vmcp.config.Duration](#vmcpconfigduration)_ | Timeout is the maximum execution time for this step | | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Type: string
Optional: \{\}
| +| `onDecline` _[vmcp.config.ElicitationResponseConfig](#vmcpconfigelicitationresponseconfig)_ | OnDecline defines the action to take when the user explicitly declines the elicitation
Only used when Type is "elicitation" | | Optional: \{\}
| +| `onCancel` _[vmcp.config.ElicitationResponseConfig](#vmcpconfigelicitationresponseconfig)_ | OnCancel defines the action to take when the user cancels/dismisses the elicitation
Only used when Type is "elicitation" | | Optional: \{\}
| +| `defaultResults` _[pkg.json.Map](#pkgjsonmap)_ | DefaultResults provides fallback output values when this step is skipped
(due to condition evaluating to false) or fails (when onError.action is "continue").
Each key corresponds to an output field name referenced by downstream steps.
Required if the step may be skipped AND downstream steps reference this step's output. | | Schemaless: \{\}
Optional: \{\}
| + +#### vmcp.config.WorkloadToolConfig + +WorkloadToolConfig defines tool filtering and overrides for a specific workload. + +_Appears in:_ + +- [vmcp.config.AggregationConfig](#vmcpconfigaggregationconfig) + +| Field | Description | Default | Validation | +| ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `workload` _string_ | Workload is the name of the backend MCPServer workload. | | Required: \{\}
| +| `toolConfigRef` _[vmcp.config.ToolConfigRef](#vmcpconfigtoolconfigref)_ | ToolConfigRef references an MCPToolConfig resource for tool filtering and renaming.
If specified, Filter and Overrides are ignored.
Only used when running in Kubernetes with the operator. | | Optional: \{\}
| +| `filter` _string array_ | Filter is an allow-list of tool names to advertise to MCP clients.
Tools NOT in this list are hidden from clients (not in tools/list response)
but remain available in the routing table for composite tools to use.
This enables selective exposure of backend tools while allowing composite
workflows to orchestrate all backend capabilities.
Only used if ToolConfigRef is not specified. | | Optional: \{\}
| +| `overrides` _object (keys:string, values:[vmcp.config.ToolOverride](#vmcpconfigtooloverride))_ | Overrides is an inline map of tool overrides for renaming and description changes.
Overrides are applied to tools before conflict resolution and affect both
advertising and routing (the overridden name is used everywhere).
Only used if ToolConfigRef is not specified. | | Optional: \{\}
| +| `excludeAll` _boolean_ | ExcludeAll hides all tools from this workload from MCP clients when true.
Hidden tools are NOT advertised in tools/list responses, but they ARE
available in the routing table for composite tools to use.
This enables the use case where you want to hide raw backend tools from
direct client access while exposing curated composite tool workflows. | | Optional: \{\}
| + +## toolhive.stacklok.dev/telemetry + +#### pkg.telemetry.Config + +Config holds the configuration for OpenTelemetry instrumentation. + +_Appears in:_ + +- [vmcp.config.Config](#vmcpconfigconfig) + +| Field | Description | Default | Validation | +| -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `endpoint` _string_ | Endpoint is the OTLP endpoint URL | | Optional: \{\}
| +| `serviceName` _string_ | ServiceName is the service name for telemetry.
When omitted, defaults to the server name (e.g., VirtualMCPServer name). | | Optional: \{\}
| +| `serviceVersion` _string_ | ServiceVersion is the service version for telemetry.
When omitted, defaults to the ToolHive version. | | Optional: \{\}
| +| `tracingEnabled` _boolean_ | TracingEnabled controls whether distributed tracing is enabled.
When false, no tracer provider is created even if an endpoint is configured. | false | Optional: \{\}
| +| `metricsEnabled` _boolean_ | MetricsEnabled controls whether OTLP metrics are enabled.
When false, OTLP metrics are not sent even if an endpoint is configured.
This is independent of EnablePrometheusMetricsPath. | false | Optional: \{\}
| +| `samplingRate` _string_ | SamplingRate is the trace sampling rate (0.0-1.0) as a string.
Only used when TracingEnabled is true.
Example: "0.05" for 5% sampling. | 0.05 | Optional: \{\}
| +| `headers` _object (keys:string, values:string)_ | Headers contains authentication headers for the OTLP endpoint. | | Optional: \{\}
| +| `insecure` _boolean_ | Insecure indicates whether to use HTTP instead of HTTPS for the OTLP endpoint. | false | Optional: \{\}
| +| `enablePrometheusMetricsPath` _boolean_ | EnablePrometheusMetricsPath controls whether to expose Prometheus-style /metrics endpoint.
The metrics are served on the main transport port at /metrics.
This is separate from OTLP metrics which are sent to the Endpoint. | false | Optional: \{\}
| +| `environmentVariables` _string array_ | EnvironmentVariables is a list of environment variable names that should be
included in telemetry spans as attributes. Only variables in this list will
be read from the host machine and included in spans for observability.
Example: ["NODE_ENV", "DEPLOYMENT_ENV", "SERVICE_VERSION"] | | Optional: \{\}
| +| `customAttributes` _object (keys:string, values:string)_ | CustomAttributes contains custom resource attributes to be added to all telemetry signals.
These are parsed from CLI flags (--otel-custom-attributes) or environment variables
(OTEL_RESOURCE_ATTRIBUTES) as key=value pairs. | | Optional: \{\}
| +| `useLegacyAttributes` _boolean_ | UseLegacyAttributes controls whether legacy (pre-MCP OTEL semconv) attribute names
are emitted alongside the new standard attribute names. When true, spans include both
old and new attribute names for backward compatibility with existing dashboards.
Currently defaults to true; this will change to false in a future release. | true | Optional: \{\}
| + +## toolhive.stacklok.dev/v1alpha1 + +### Resource Types + +- [api.v1alpha1.EmbeddingServer](#apiv1alpha1embeddingserver) +- [api.v1alpha1.EmbeddingServerList](#apiv1alpha1embeddingserverlist) +- [api.v1alpha1.MCPExternalAuthConfig](#apiv1alpha1mcpexternalauthconfig) +- [api.v1alpha1.MCPExternalAuthConfigList](#apiv1alpha1mcpexternalauthconfiglist) +- [api.v1alpha1.MCPGroup](#apiv1alpha1mcpgroup) +- [api.v1alpha1.MCPGroupList](#apiv1alpha1mcpgrouplist) +- [api.v1alpha1.MCPRegistry](#apiv1alpha1mcpregistry) +- [api.v1alpha1.MCPRegistryList](#apiv1alpha1mcpregistrylist) +- [api.v1alpha1.MCPRemoteProxy](#apiv1alpha1mcpremoteproxy) +- [api.v1alpha1.MCPRemoteProxyList](#apiv1alpha1mcpremoteproxylist) +- [api.v1alpha1.MCPServer](#apiv1alpha1mcpserver) +- [api.v1alpha1.MCPServerList](#apiv1alpha1mcpserverlist) +- [api.v1alpha1.MCPToolConfig](#apiv1alpha1mcptoolconfig) +- [api.v1alpha1.MCPToolConfigList](#apiv1alpha1mcptoolconfiglist) +- [api.v1alpha1.VirtualMCPCompositeToolDefinition](#apiv1alpha1virtualmcpcompositetooldefinition) +- [api.v1alpha1.VirtualMCPCompositeToolDefinitionList](#apiv1alpha1virtualmcpcompositetooldefinitionlist) +- [api.v1alpha1.VirtualMCPServer](#apiv1alpha1virtualmcpserver) +- [api.v1alpha1.VirtualMCPServerList](#apiv1alpha1virtualmcpserverlist) + +#### api.v1alpha1.APIPhase + +_Underlying type:_ _string_ + +APIPhase represents the API service state + +_Validation:_ + +- Enum: [NotStarted Deploying Ready Unhealthy Error] + +_Appears in:_ + +- [api.v1alpha1.APIStatus](#apiv1alpha1apistatus) + +| Field | Description | +| ------------ | ------------------------------------------------------------------ | +| `NotStarted` | APIPhaseNotStarted means API deployment has not been created
| +| `Deploying` | APIPhaseDeploying means API is being deployed
| +| `Ready` | APIPhaseReady means API is ready to serve requests
| +| `Unhealthy` | APIPhaseUnhealthy means API is deployed but not healthy
| +| `Error` | APIPhaseError means API deployment failed
| + +#### api.v1alpha1.APISource + +APISource defines API source configuration for ToolHive Registry APIs +Phase 1: Supports ToolHive API endpoints (no pagination) +Phase 2: Will add support for upstream MCP Registry API with pagination + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryConfig](#apiv1alpha1mcpregistryconfig) + +| Field | Description | Default | Validation | +| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | ---------------------------------------------------------------------- | +| `endpoint` _string_ | Endpoint is the base API URL (without path)
The controller will append the appropriate paths:
Phase 1 (ToolHive API):
- /v0/servers - List all servers (single response, no pagination)
- /v0/servers/\{name\} - Get specific server (future)
- /v0/info - Get registry metadata (future)
Example: "http://my-registry-api.default.svc.cluster.local/api" | | MinLength: 1
Pattern: `^https?://.*`
Required: \{\}
| + +#### api.v1alpha1.APIStatus + +APIStatus provides detailed information about the API service + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryStatus](#apiv1alpha1mcpregistrystatus) + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | ------- | --------------------------------------------------------- | +| `phase` _[api.v1alpha1.APIPhase](#apiv1alpha1apiphase)_ | Phase represents the current API service phase | | Enum: [NotStarted Deploying Ready Unhealthy Error]
| +| `message` _string_ | Message provides additional information about the API status | | Optional: \{\}
| +| `endpoint` _string_ | Endpoint is the URL where the API is accessible | | Optional: \{\}
| +| `readySince` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#time-v1-meta)_ | ReadySince is the timestamp when the API became ready | | Optional: \{\}
| + +#### api.v1alpha1.AWSStsConfig + +AWSStsConfig holds configuration for AWS STS authentication with SigV4 request signing. +This configuration exchanges incoming authentication tokens (typically OIDC JWT) for AWS STS +temporary credentials, then signs requests to AWS services using SigV4. + +_Appears in:_ + +- [api.v1alpha1.MCPExternalAuthConfigSpec](#apiv1alpha1mcpexternalauthconfigspec) + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | -------------------------------------------------------------------------------------------------------- | +| `region` _string_ | Region is the AWS region for the STS endpoint and service (e.g., "us-east-1", "eu-west-1") | | MinLength: 1
Pattern: `^[a-z]\{2\}(-[a-z]+)+-\d+$`
Required: \{\}
| +| `service` _string_ | Service is the AWS service name for SigV4 signing
Defaults to "aws-mcp" for AWS MCP Server endpoints | aws-mcp | Optional: \{\}
| +| `fallbackRoleArn` _string_ | FallbackRoleArn is the IAM role ARN to assume when no role mappings match
Used as the default role when RoleMappings is empty or no mapping matches
At least one of FallbackRoleArn or RoleMappings must be configured (enforced by webhook) | | Pattern: `^arn:(aws\|aws-cn\|aws-us-gov):iam::\d\{12\}:role/[\w+=,.@\-_/]+$`
Optional: \{\}
| +| `roleMappings` _[api.v1alpha1.RoleMapping](#apiv1alpha1rolemapping) array_ | RoleMappings defines claim-based role selection rules
Allows mapping JWT claims (e.g., groups, roles) to specific IAM roles
Lower priority values are evaluated first (higher priority) | | Optional: \{\}
| +| `roleClaim` _string_ | RoleClaim is the JWT claim to use for role mapping evaluation
Defaults to "groups" to match common OIDC group claims | groups | Optional: \{\}
| +| `sessionDuration` _integer_ | SessionDuration is the duration in seconds for the STS session
Must be between 900 (15 minutes) and 43200 (12 hours)
Defaults to 3600 (1 hour) if not specified | 3600 | Maximum: 43200
Minimum: 900
Optional: \{\}
| +| `sessionNameClaim` _string_ | SessionNameClaim is the JWT claim to use for role session name
Defaults to "sub" to use the subject claim | sub | Optional: \{\}
| + +#### api.v1alpha1.AuditConfig + +AuditConfig defines audit logging configuration for the MCP server + +_Appears in:_ + +- [api.v1alpha1.MCPRemoteProxySpec](#apiv1alpha1mcpremoteproxyspec) +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) + +| Field | Description | Default | Validation | +| ------------------- | ------------------------------------------------------------------------------------------------------------------ | ------- | --------------------- | +| `enabled` _boolean_ | Enabled controls whether audit logging is enabled
When true, enables audit logging with default configuration | false | Optional: \{\}
| + +#### api.v1alpha1.AuthServerStorageConfig + +AuthServerStorageConfig configures the storage backend for the embedded auth server. + +_Appears in:_ + +- [api.v1alpha1.EmbeddedAuthServerConfig](#apiv1alpha1embeddedauthserverconfig) + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ------- | --------------------------- | +| `type` _[api.v1alpha1.AuthServerStorageType](#apiv1alpha1authserverstoragetype)_ | Type specifies the storage backend type.
Valid values: "memory" (default), "redis". | memory | Enum: [memory redis]
| +| `redis` _[api.v1alpha1.RedisStorageConfig](#apiv1alpha1redisstorageconfig)_ | Redis configures the Redis storage backend.
Required when type is "redis". | | Optional: \{\}
| + +#### api.v1alpha1.AuthServerStorageType + +_Underlying type:_ _string_ + +AuthServerStorageType represents the type of storage backend for the embedded auth server + +_Appears in:_ + +- [api.v1alpha1.AuthServerStorageConfig](#apiv1alpha1authserverstorageconfig) + +| Field | Description | +| -------- | ---------------------------------------------------------------------------- | +| `memory` | AuthServerStorageTypeMemory is the in-memory storage backend (default)
| +| `redis` | AuthServerStorageTypeRedis is the Redis storage backend
| + +#### api.v1alpha1.AuthzConfigRef + +AuthzConfigRef defines a reference to authorization configuration + +_Appears in:_ + +- [api.v1alpha1.IncomingAuthConfig](#apiv1alpha1incomingauthconfig) +- [api.v1alpha1.MCPRemoteProxySpec](#apiv1alpha1mcpremoteproxyspec) +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) + +| Field | Description | Default | Validation | +| ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | --------- | ------------------------------- | +| `type` _string_ | Type is the type of authorization configuration | configMap | Enum: [configMap inline]
| +| `configMap` _[api.v1alpha1.ConfigMapAuthzRef](#apiv1alpha1configmapauthzref)_ | ConfigMap references a ConfigMap containing authorization configuration
Only used when Type is "configMap" | | Optional: \{\}
| +| `inline` _[api.v1alpha1.InlineAuthzConfig](#apiv1alpha1inlineauthzconfig)_ | Inline contains direct authorization configuration
Only used when Type is "inline" | | Optional: \{\}
| + +#### api.v1alpha1.BackendAuthConfig + +BackendAuthConfig defines authentication configuration for a backend MCPServer + +_Appears in:_ + +- [api.v1alpha1.OutgoingAuthConfig](#apiv1alpha1outgoingauthconfig) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------- | ----------------------------------------------------------------------- | +| `type` _string_ | Type defines the authentication type | | Enum: [discovered external_auth_config_ref]
Required: \{\}
| +| `externalAuthConfigRef` _[api.v1alpha1.ExternalAuthConfigRef](#apiv1alpha1externalauthconfigref)_ | ExternalAuthConfigRef references an MCPExternalAuthConfig resource
Only used when Type is "external_auth_config_ref" | | Optional: \{\}
| + +#### api.v1alpha1.BearerTokenConfig + +BearerTokenConfig holds configuration for bearer token authentication. +This allows authenticating to remote MCP servers using bearer tokens stored in Kubernetes Secrets. +For security reasons, only secret references are supported (no plaintext values). + +_Appears in:_ + +- [api.v1alpha1.MCPExternalAuthConfigSpec](#apiv1alpha1mcpexternalauthconfigspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------- | ------- | --------------------- | +| `tokenSecretRef` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref)_ | TokenSecretRef references a Kubernetes Secret containing the bearer token | | Required: \{\}
| + +#### api.v1alpha1.CABundleSource + +CABundleSource defines a source for CA certificate bundles. + +_Appears in:_ + +- [api.v1alpha1.ConfigMapOIDCRef](#apiv1alpha1configmapoidcref) +- [api.v1alpha1.InlineOIDCConfig](#apiv1alpha1inlineoidcconfig) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `configMapRef` _[ConfigMapKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#configmapkeyselector-v1-core)_ | ConfigMapRef references a ConfigMap containing the CA certificate bundle.
If Key is not specified, it defaults to "ca.crt". | | Optional: \{\}
| + +#### api.v1alpha1.ConfigMapAuthzRef + +ConfigMapAuthzRef references a ConfigMap containing authorization configuration + +_Appears in:_ + +- [api.v1alpha1.AuthzConfigRef](#apiv1alpha1authzconfigref) + +| Field | Description | Default | Validation | +| --------------- | ----------------------------------------------------------------------------- | ---------- | --------------------- | +| `name` _string_ | Name is the name of the ConfigMap | | Required: \{\}
| +| `key` _string_ | Key is the key in the ConfigMap that contains the authorization configuration | authz.json | Optional: \{\}
| + +#### api.v1alpha1.ConfigMapOIDCRef + +ConfigMapOIDCRef references a ConfigMap containing OIDC configuration + +_Appears in:_ + +- [api.v1alpha1.OIDCConfigRef](#apiv1alpha1oidcconfigref) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | --------------------- | +| `name` _string_ | Name is the name of the ConfigMap | | Required: \{\}
| +| `key` _string_ | Key is the key in the ConfigMap that contains the OIDC configuration | oidc.json | Optional: \{\}
| +| `caBundleRef` _[api.v1alpha1.CABundleSource](#apiv1alpha1cabundlesource)_ | CABundleRef references a ConfigMap containing the CA certificate bundle.
When specified, ToolHive auto-mounts the ConfigMap and auto-computes ThvCABundlePath.
If the ConfigMap data contains an explicit thvCABundlePath key, it takes precedence. | | Optional: \{\}
| + +#### api.v1alpha1.EmbeddedAuthServerConfig + +EmbeddedAuthServerConfig holds configuration for the embedded OAuth2/OIDC authorization server. +This enables running an authorization server that delegates authentication to upstream IDPs. + +_Appears in:_ + +- [api.v1alpha1.MCPExternalAuthConfigSpec](#apiv1alpha1mcpexternalauthconfigspec) + +| Field | Description | Default | Validation | +| ----------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------ | +| `issuer` _string_ | Issuer is the issuer identifier for this authorization server.
This will be included in the "iss" claim of issued tokens.
Must be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash (per RFC 8414). | | Pattern: `^https?://[^\s?#]+[^/\s?#]$`
Required: \{\}
| +| `signingKeySecretRefs` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref) array_ | SigningKeySecretRefs references Kubernetes Secrets containing signing keys for JWT operations.
Supports key rotation by allowing multiple keys (oldest keys are used for verification only).
If not specified, an ephemeral signing key will be auto-generated (development only -
JWTs will be invalid after restart). | | MaxItems: 5
Optional: \{\}
| +| `hmacSecretRefs` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref) array_ | HMACSecretRefs references Kubernetes Secrets containing symmetric secrets for signing
authorization codes and refresh tokens (opaque tokens).
Current secret must be at least 32 bytes and cryptographically random.
Supports secret rotation via multiple entries (first is current, rest are for verification).
If not specified, an ephemeral secret will be auto-generated (development only -
auth codes and refresh tokens will be invalid after restart). | | Optional: \{\}
| +| `tokenLifespans` _[api.v1alpha1.TokenLifespanConfig](#apiv1alpha1tokenlifespanconfig)_ | TokenLifespans configures the duration that various tokens are valid.
If not specified, defaults are applied (access: 1h, refresh: 7d, authCode: 10m). | | Optional: \{\}
| +| `upstreamProviders` _[api.v1alpha1.UpstreamProviderConfig](#apiv1alpha1upstreamproviderconfig) array_ | UpstreamProviders configures connections to upstream Identity Providers.
The embedded auth server delegates authentication to these providers.
Currently only a single upstream provider is supported (validated at runtime). | | MinItems: 1
Required: \{\}
| +| `storage` _[api.v1alpha1.AuthServerStorageConfig](#apiv1alpha1authserverstorageconfig)_ | Storage configures the storage backend for the embedded auth server.
If not specified, defaults to in-memory storage. | | Optional: \{\}
| + +#### api.v1alpha1.EmbeddingResourceOverrides + +EmbeddingResourceOverrides defines overrides for annotations and labels on created resources + +_Appears in:_ + +- [api.v1alpha1.EmbeddingServerSpec](#apiv1alpha1embeddingserverspec) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | ------- | --------------------- | +| `statefulSet` _[api.v1alpha1.EmbeddingStatefulSetOverrides](#apiv1alpha1embeddingstatefulsetoverrides)_ | StatefulSet defines overrides for the StatefulSet resource | | Optional: \{\}
| +| `service` _[api.v1alpha1.ResourceMetadataOverrides](#apiv1alpha1resourcemetadataoverrides)_ | Service defines overrides for the Service resource | | Optional: \{\}
| +| `persistentVolumeClaim` _[api.v1alpha1.ResourceMetadataOverrides](#apiv1alpha1resourcemetadataoverrides)_ | PersistentVolumeClaim defines overrides for the PVC resource | | Optional: \{\}
| + +#### api.v1alpha1.EmbeddingServer + +EmbeddingServer is the Schema for the embeddingservers API + +_Appears in:_ + +- [api.v1alpha1.EmbeddingServerList](#apiv1alpha1embeddingserverlist) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `EmbeddingServer` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `spec` _[api.v1alpha1.EmbeddingServerSpec](#apiv1alpha1embeddingserverspec)_ | | | | +| `status` _[api.v1alpha1.EmbeddingServerStatus](#apiv1alpha1embeddingserverstatus)_ | | | | + +#### api.v1alpha1.EmbeddingServerList + +EmbeddingServerList contains a list of EmbeddingServer + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `EmbeddingServerList` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `items` _[api.v1alpha1.EmbeddingServer](#apiv1alpha1embeddingserver) array_ | | | | + +#### api.v1alpha1.EmbeddingServerPhase + +_Underlying type:_ _string_ + +EmbeddingServerPhase is the phase of the EmbeddingServer + +_Validation:_ + +- Enum: [Pending Downloading Running Failed Terminating] + +_Appears in:_ + +- [api.v1alpha1.EmbeddingServerStatus](#apiv1alpha1embeddingserverstatus) + +| Field | Description | +| ------------- | -------------------------------------------------------------------------------- | +| `Pending` | EmbeddingServerPhasePending means the EmbeddingServer is being created
| +| `Downloading` | EmbeddingServerPhaseDownloading means the model is being downloaded
| +| `Running` | EmbeddingServerPhaseRunning means the EmbeddingServer is running and ready
| +| `Failed` | EmbeddingServerPhaseFailed means the EmbeddingServer failed to start
| +| `Terminating` | EmbeddingServerPhaseTerminating means the EmbeddingServer is being deleted
| + +#### api.v1alpha1.EmbeddingServerRef + +EmbeddingServerRef references an existing EmbeddingServer resource by name. +This follows the same pattern as ExternalAuthConfigRef and ToolConfigRef. + +_Appears in:_ + +- [api.v1alpha1.VirtualMCPServerSpec](#apiv1alpha1virtualmcpserverspec) + +| Field | Description | Default | Validation | +| --------------- | ------------------------------------------------ | ------- | --------------------- | +| `name` _string_ | Name is the name of the EmbeddingServer resource | | Required: \{\}
| + +#### api.v1alpha1.EmbeddingServerSpec + +EmbeddingServerSpec defines the desired state of EmbeddingServer + +_Appears in:_ + +- [api.v1alpha1.EmbeddingServer](#apiv1alpha1embeddingserver) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------- | +| `model` _string_ | Model is the HuggingFace embedding model to use (e.g., "sentence-transformers/all-MiniLM-L6-v2") | BAAI/bge-small-en-v1.5 | Optional: \{\}
| +| `hfTokenSecretRef` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref)_ | HFTokenSecretRef is a reference to a Kubernetes Secret containing the huggingface token.
If provided, the secret value will be provided to the embedding server for authentication with huggingface. | | Optional: \{\}
| +| `image` _string_ | Image is the container image for the embedding inference server.
Images must be from HuggingFace Text Embeddings Inference (https://github.com/huggingface/text-embeddings-inference). | ghcr.io/huggingface/text-embeddings-inference:cpu-latest | Optional: \{\}
| +| `imagePullPolicy` _string_ | ImagePullPolicy defines the pull policy for the container image | IfNotPresent | Enum: [Always Never IfNotPresent]
Optional: \{\}
| +| `port` _integer_ | Port is the port to expose the embedding service on | 8080 | Maximum: 65535
Minimum: 1
| +| `args` _string array_ | Args are additional arguments to pass to the embedding inference server | | Optional: \{\}
| +| `env` _[api.v1alpha1.EnvVar](#apiv1alpha1envvar) array_ | Env are environment variables to set in the container | | Optional: \{\}
| +| `resources` _[api.v1alpha1.ResourceRequirements](#apiv1alpha1resourcerequirements)_ | Resources defines compute resources for the embedding server | | Optional: \{\}
| +| `modelCache` _[api.v1alpha1.ModelCacheConfig](#apiv1alpha1modelcacheconfig)_ | ModelCache configures persistent storage for downloaded models
When enabled, models are cached in a PVC and reused across pod restarts | | Optional: \{\}
| +| `podTemplateSpec` _[RawExtension](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#rawextension-runtime-pkg)_ | PodTemplateSpec allows customizing the pod (node selection, tolerations, etc.)
This field accepts a PodTemplateSpec object as JSON/YAML.
Note that to modify the specific container the embedding server runs in, you must specify
the 'embedding' container name in the PodTemplateSpec. | | Type: object
Optional: \{\}
| +| `resourceOverrides` _[api.v1alpha1.EmbeddingResourceOverrides](#apiv1alpha1embeddingresourceoverrides)_ | ResourceOverrides allows overriding annotations and labels for resources created by the operator | | Optional: \{\}
| +| `replicas` _integer_ | Replicas is the number of embedding server replicas to run | 1 | Minimum: 1
Optional: \{\}
| + +#### api.v1alpha1.EmbeddingServerStatus + +EmbeddingServerStatus defines the observed state of EmbeddingServer + +_Appears in:_ + +- [api.v1alpha1.EmbeddingServer](#apiv1alpha1embeddingserver) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------------------------- | +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#condition-v1-meta) array_ | Conditions represent the latest available observations of the EmbeddingServer's state | | Optional: \{\}
| +| `phase` _[api.v1alpha1.EmbeddingServerPhase](#apiv1alpha1embeddingserverphase)_ | Phase is the current phase of the EmbeddingServer | | Enum: [Pending Downloading Running Failed Terminating]
Optional: \{\}
| +| `message` _string_ | Message provides additional information about the current phase | | Optional: \{\}
| +| `url` _string_ | URL is the URL where the embedding service can be accessed | | Optional: \{\}
| +| `readyReplicas` _integer_ | ReadyReplicas is the number of ready replicas | | Optional: \{\}
| +| `observedGeneration` _integer_ | ObservedGeneration reflects the generation most recently observed by the controller | | Optional: \{\}
| + +#### api.v1alpha1.EmbeddingStatefulSetOverrides + +EmbeddingStatefulSetOverrides defines overrides specific to the embedding statefulset + +_Appears in:_ + +- [api.v1alpha1.EmbeddingResourceOverrides](#apiv1alpha1embeddingresourceoverrides) + +| Field | Description | Default | Validation | +| ---------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ------- | --------------------- | +| `annotations` _object (keys:string, values:string)_ | Annotations to add or override on the resource | | Optional: \{\}
| +| `labels` _object (keys:string, values:string)_ | Labels to add or override on the resource | | Optional: \{\}
| +| `podTemplateMetadataOverrides` _[api.v1alpha1.ResourceMetadataOverrides](#apiv1alpha1resourcemetadataoverrides)_ | PodTemplateMetadataOverrides defines metadata overrides for the pod template | | Optional: \{\}
| + +#### api.v1alpha1.EnvVar + +EnvVar represents an environment variable in a container + +_Appears in:_ + +- [api.v1alpha1.EmbeddingServerSpec](#apiv1alpha1embeddingserverspec) +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) +- [api.v1alpha1.ProxyDeploymentOverrides](#apiv1alpha1proxydeploymentoverrides) + +| Field | Description | Default | Validation | +| ---------------- | --------------------------------- | ------- | --------------------- | +| `name` _string_ | Name of the environment variable | | Required: \{\}
| +| `value` _string_ | Value of the environment variable | | Required: \{\}
| + +#### api.v1alpha1.ExternalAuthConfigRef + +ExternalAuthConfigRef defines a reference to a MCPExternalAuthConfig resource. +The referenced MCPExternalAuthConfig must be in the same namespace as the MCPServer. + +_Appears in:_ + +- [api.v1alpha1.BackendAuthConfig](#apiv1alpha1backendauthconfig) +- [api.v1alpha1.MCPRemoteProxySpec](#apiv1alpha1mcpremoteproxyspec) +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) + +| Field | Description | Default | Validation | +| --------------- | ------------------------------------------------------ | ------- | --------------------- | +| `name` _string_ | Name is the name of the MCPExternalAuthConfig resource | | Required: \{\}
| + +#### api.v1alpha1.ExternalAuthType + +_Underlying type:_ _string_ + +ExternalAuthType represents the type of external authentication + +_Appears in:_ + +- [api.v1alpha1.MCPExternalAuthConfigSpec](#apiv1alpha1mcpexternalauthconfigspec) + +| Field | Description | +| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `tokenExchange` | ExternalAuthTypeTokenExchange is the type for RFC-8693 token exchange
| +| `headerInjection` | ExternalAuthTypeHeaderInjection is the type for custom header injection
| +| `bearerToken` | ExternalAuthTypeBearerToken is the type for bearer token authentication
This allows authenticating to remote MCP servers using bearer tokens stored in Kubernetes Secrets
| +| `unauthenticated` | ExternalAuthTypeUnauthenticated is the type for no authentication
This should only be used for backends on trusted networks (e.g., localhost, VPC)
or when authentication is handled by network-level security
| +| `embeddedAuthServer` | ExternalAuthTypeEmbeddedAuthServer is the type for embedded OAuth2/OIDC authorization server
This enables running an embedded auth server that delegates to upstream IDPs
| +| `awsSts` | ExternalAuthTypeAWSSts is the type for AWS STS authentication
| + +#### api.v1alpha1.GitAuthConfig + +GitAuthConfig defines authentication settings for private Git repositories. +Uses HTTP Basic authentication with username and password/token. +The password is stored in a Kubernetes Secret and mounted as a file +for the registry server to read. + +_Appears in:_ + +- [api.v1alpha1.GitSource](#apiv1alpha1gitsource) + +| Field | Description | Default | Validation | +| ----------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------- | +| `username` _string_ | Username is the Git username for HTTP Basic authentication.
For GitHub/GitLab token-based auth, this is typically the literal string "git"
or the token itself depending on the provider. | | MinLength: 1
Required: \{\}
| +| `passwordSecretRef` _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#secretkeyselector-v1-core)_ | PasswordSecretRef references a Kubernetes Secret containing the password or token
for Git authentication. The secret value will be mounted as a file and its path
passed to the registry server via the git.auth.passwordFile configuration.
Example secret:
apiVersion: v1
kind: Secret
metadata:
name: git-credentials
stringData:
token:
Then reference it as:
passwordSecretRef:
name: git-credentials
key: token | | Required: \{\}
| + +#### api.v1alpha1.GitSource + +GitSource defines Git repository source configuration + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryConfig](#apiv1alpha1mcpregistryconfig) + +| Field | Description | Default | Validation | +| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | -------------------------------------------------------------------------------------------------------- | +| `repository` _string_ | Repository is the Git repository URL (HTTP/HTTPS/SSH) | | MinLength: 1
Pattern: `^(file:///\|https?://\|git@\|ssh://\|git://).*`
Required: \{\}
| +| `branch` _string_ | Branch is the Git branch to use (mutually exclusive with Tag and Commit) | | MinLength: 1
Optional: \{\}
| +| `tag` _string_ | Tag is the Git tag to use (mutually exclusive with Branch and Commit) | | MinLength: 1
Optional: \{\}
| +| `commit` _string_ | Commit is the Git commit SHA to use (mutually exclusive with Branch and Tag) | | MinLength: 1
Optional: \{\}
| +| `path` _string_ | Path is the path to the registry file within the repository | registry.json | Pattern: `^.*\.json$`
Optional: \{\}
| +| `auth` _[api.v1alpha1.GitAuthConfig](#apiv1alpha1gitauthconfig)_ | Auth defines optional authentication for private Git repositories.
When specified, enables HTTP Basic authentication using the provided
username and password/token from a Kubernetes Secret. | | Optional: \{\}
| + +#### api.v1alpha1.HeaderForwardConfig + +HeaderForwardConfig defines header forward configuration for remote servers. + +_Appears in:_ + +- [api.v1alpha1.MCPRemoteProxySpec](#apiv1alpha1mcpremoteproxyspec) + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | --------------------- | +| `addPlaintextHeaders` _object (keys:string, values:string)_ | AddPlaintextHeaders is a map of header names to literal values to inject into requests.
WARNING: Values are stored in plaintext and visible via kubectl commands.
Use addHeadersFromSecret for sensitive data like API keys or tokens. | | Optional: \{\}
| +| `addHeadersFromSecret` _[api.v1alpha1.HeaderFromSecret](#apiv1alpha1headerfromsecret) array_ | AddHeadersFromSecret references Kubernetes Secrets for sensitive header values. | | Optional: \{\}
| + +#### api.v1alpha1.HeaderFromSecret + +HeaderFromSecret defines a header whose value comes from a Kubernetes Secret. + +_Appears in:_ + +- [api.v1alpha1.HeaderForwardConfig](#apiv1alpha1headerforwardconfig) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------ | ------- | ------------------------------------------------------------- | +| `headerName` _string_ | HeaderName is the HTTP header name (e.g., "X-API-Key") | | MaxLength: 255
MinLength: 1
Required: \{\}
| +| `valueSecretRef` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref)_ | ValueSecretRef references the Secret and key containing the header value | | Required: \{\}
| + +#### api.v1alpha1.HeaderInjectionConfig + +HeaderInjectionConfig holds configuration for custom HTTP header injection authentication. +This allows injecting a secret-based header value into requests to backend MCP servers. +For security reasons, only secret references are supported (no plaintext values). + +_Appears in:_ + +- [api.v1alpha1.MCPExternalAuthConfigSpec](#apiv1alpha1mcpexternalauthconfigspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------- | ------- | ---------------------------------------- | +| `headerName` _string_ | HeaderName is the name of the HTTP header to inject | | MinLength: 1
Required: \{\}
| +| `valueSecretRef` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref)_ | ValueSecretRef references a Kubernetes Secret containing the header value | | Required: \{\}
| + +#### api.v1alpha1.IncomingAuthConfig + +IncomingAuthConfig configures authentication for clients connecting to the Virtual MCP server + +_Appears in:_ + +- [api.v1alpha1.VirtualMCPServerSpec](#apiv1alpha1virtualmcpserverspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ------- | -------------------------------------------------- | +| `type` _string_ | Type defines the authentication type: anonymous or oidc
When no authentication is required, explicitly set this to "anonymous" | | Enum: [anonymous oidc]
Required: \{\}
| +| `oidcConfig` _[api.v1alpha1.OIDCConfigRef](#apiv1alpha1oidcconfigref)_ | OIDCConfig defines OIDC authentication configuration
Reuses MCPServer OIDC patterns | | Optional: \{\}
| +| `authzConfig` _[api.v1alpha1.AuthzConfigRef](#apiv1alpha1authzconfigref)_ | AuthzConfig defines authorization policy configuration
Reuses MCPServer authz patterns | | Optional: \{\}
| + +#### api.v1alpha1.InlineAuthzConfig + +InlineAuthzConfig contains direct authorization configuration + +_Appears in:_ + +- [api.v1alpha1.AuthzConfigRef](#apiv1alpha1authzconfigref) + +| Field | Description | Default | Validation | +| ------------------------- | --------------------------------------------------------- | ------- | --------------------------------------- | +| `policies` _string array_ | Policies is a list of Cedar policy strings | | MinItems: 1
Required: \{\}
| +| `entitiesJson` _string_ | EntitiesJSON is a JSON string representing Cedar entities | [] | Optional: \{\}
| + +#### api.v1alpha1.InlineOIDCConfig + +InlineOIDCConfig contains direct OIDC configuration + +_Appears in:_ + +- [api.v1alpha1.OIDCConfigRef](#apiv1alpha1oidcconfigref) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `issuer` _string_ | Issuer is the OIDC issuer URL | | Required: \{\}
| +| `audience` _string_ | Audience is the expected audience for the token | | Optional: \{\}
| +| `jwksUrl` _string_ | JWKSURL is the URL to fetch the JWKS from | | Optional: \{\}
| +| `introspectionUrl` _string_ | IntrospectionURL is the URL for token introspection endpoint | | Optional: \{\}
| +| `clientId` _string_ | ClientID is the OIDC client ID | | Optional: \{\}
| +| `clientSecret` _string_ | ClientSecret is the client secret for introspection (optional)
Deprecated: Use ClientSecretRef instead for better security | | Optional: \{\}
| +| `clientSecretRef` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref)_ | ClientSecretRef is a reference to a Kubernetes Secret containing the client secret
If both ClientSecret and ClientSecretRef are provided, ClientSecretRef takes precedence | | Optional: \{\}
| +| `thvCABundlePath` _string_ | ThvCABundlePath is the path to CA certificate bundle file for HTTPS requests.
Deprecated: Use CABundleRef instead. ThvCABundlePath requires the CA bundle to
already exist in the proxy runner container (e.g., Kubernetes service account CA at
/var/run/secrets/kubernetes.io/serviceaccount/ca.crt). For custom CA certificates,
use CABundleRef which automatically mounts the ConfigMap and computes the path.
This field will be removed when the API graduates to v1beta1. | | Optional: \{\}
| +| `caBundleRef` _[api.v1alpha1.CABundleSource](#apiv1alpha1cabundlesource)_ | CABundleRef references a ConfigMap containing the CA certificate bundle.
When specified, ToolHive auto-mounts the ConfigMap and auto-computes ThvCABundlePath.
If ThvCABundlePath is explicitly set, it takes precedence over CABundleRef. | | Optional: \{\}
| +| `jwksAuthTokenPath` _string_ | JWKSAuthTokenPath is the path to file containing bearer token for JWKS/OIDC requests
The file must be mounted into the pod (e.g., via Secret volume) | | Optional: \{\}
| +| `jwksAllowPrivateIP` _boolean_ | JWKSAllowPrivateIP allows JWKS/OIDC endpoints on private IP addresses
Use with caution - only enable for trusted internal IDPs | false | Optional: \{\}
| +| `protectedResourceAllowPrivateIP` _boolean_ | ProtectedResourceAllowPrivateIP allows protected resource endpoint on private IP addresses
Use with caution - only enable for trusted internal IDPs or testing | false | Optional: \{\}
| +| `insecureAllowHTTP` _boolean_ | InsecureAllowHTTP allows HTTP (non-HTTPS) OIDC issuers for development/testing
WARNING: This is insecure and should NEVER be used in production
Only enable for local development, testing, or trusted internal networks | false | Optional: \{\}
| +| `scopes` _string array_ | Scopes is the list of OAuth scopes to advertise in the well-known endpoint (RFC 9728)
If empty, defaults to ["openid"] | | Optional: \{\}
| + +#### api.v1alpha1.KubernetesOIDCConfig + +KubernetesOIDCConfig configures OIDC for Kubernetes service account token validation + +_Appears in:_ + +- [api.v1alpha1.OIDCConfigRef](#apiv1alpha1oidcconfigref) + +| Field | Description | Default | Validation | +| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | --------------------- | +| `serviceAccount` _string_ | ServiceAccount is the name of the service account to validate tokens for
If empty, uses the pod's service account | | Optional: \{\}
| +| `namespace` _string_ | Namespace is the namespace of the service account
If empty, uses the MCPServer's namespace | | Optional: \{\}
| +| `audience` _string_ | Audience is the expected audience for the token | toolhive | Optional: \{\}
| +| `issuer` _string_ | Issuer is the OIDC issuer URL | https://kubernetes.default.svc | Optional: \{\}
| +| `jwksUrl` _string_ | JWKSURL is the URL to fetch the JWKS from
If empty, OIDC discovery will be used to automatically determine the JWKS URL | | Optional: \{\}
| +| `introspectionUrl` _string_ | IntrospectionURL is the URL for token introspection endpoint
If empty, OIDC discovery will be used to automatically determine the introspection URL | | Optional: \{\}
| +| `useClusterAuth` _boolean_ | UseClusterAuth enables using the Kubernetes cluster's CA bundle and service account token
When true, uses /var/run/secrets/kubernetes.io/serviceaccount/ca.crt for TLS verification
and /var/run/secrets/kubernetes.io/serviceaccount/token for bearer token authentication
Defaults to true if not specified | | Optional: \{\}
| + +#### api.v1alpha1.MCPExternalAuthConfig + +MCPExternalAuthConfig is the Schema for the mcpexternalauthconfigs API. +MCPExternalAuthConfig resources are namespace-scoped and can only be referenced by +MCPServer resources within the same namespace. Cross-namespace references +are not supported for security and isolation reasons. + +_Appears in:_ + +- [api.v1alpha1.MCPExternalAuthConfigList](#apiv1alpha1mcpexternalauthconfiglist) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `MCPExternalAuthConfig` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `spec` _[api.v1alpha1.MCPExternalAuthConfigSpec](#apiv1alpha1mcpexternalauthconfigspec)_ | | | | +| `status` _[api.v1alpha1.MCPExternalAuthConfigStatus](#apiv1alpha1mcpexternalauthconfigstatus)_ | | | | + +#### api.v1alpha1.MCPExternalAuthConfigList + +MCPExternalAuthConfigList contains a list of MCPExternalAuthConfig + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `MCPExternalAuthConfigList` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `items` _[api.v1alpha1.MCPExternalAuthConfig](#apiv1alpha1mcpexternalauthconfig) array_ | | | | + +#### api.v1alpha1.MCPExternalAuthConfigSpec + +MCPExternalAuthConfigSpec defines the desired state of MCPExternalAuthConfig. +MCPExternalAuthConfig resources are namespace-scoped and can only be referenced by +MCPServer resources in the same namespace. + +_Appears in:_ + +- [api.v1alpha1.MCPExternalAuthConfig](#apiv1alpha1mcpexternalauthconfig) + +| Field | Description | Default | Validation | +| ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------- | +| `type` _[api.v1alpha1.ExternalAuthType](#apiv1alpha1externalauthtype)_ | Type is the type of external authentication to configure | | Enum: [tokenExchange headerInjection bearerToken unauthenticated embeddedAuthServer awsSts]
Required: \{\}
| +| `tokenExchange` _[api.v1alpha1.TokenExchangeConfig](#apiv1alpha1tokenexchangeconfig)_ | TokenExchange configures RFC-8693 OAuth 2.0 Token Exchange
Only used when Type is "tokenExchange" | | Optional: \{\}
| +| `headerInjection` _[api.v1alpha1.HeaderInjectionConfig](#apiv1alpha1headerinjectionconfig)_ | HeaderInjection configures custom HTTP header injection
Only used when Type is "headerInjection" | | Optional: \{\}
| +| `bearerToken` _[api.v1alpha1.BearerTokenConfig](#apiv1alpha1bearertokenconfig)_ | BearerToken configures bearer token authentication
Only used when Type is "bearerToken" | | Optional: \{\}
| +| `embeddedAuthServer` _[api.v1alpha1.EmbeddedAuthServerConfig](#apiv1alpha1embeddedauthserverconfig)_ | EmbeddedAuthServer configures an embedded OAuth2/OIDC authorization server
Only used when Type is "embeddedAuthServer" | | Optional: \{\}
| +| `awsSts` _[api.v1alpha1.AWSStsConfig](#apiv1alpha1awsstsconfig)_ | AWSSts configures AWS STS authentication with SigV4 request signing
Only used when Type is "awsSts" | | Optional: \{\}
| + +#### api.v1alpha1.MCPExternalAuthConfigStatus + +MCPExternalAuthConfigStatus defines the observed state of MCPExternalAuthConfig + +_Appears in:_ + +- [api.v1alpha1.MCPExternalAuthConfig](#apiv1alpha1mcpexternalauthconfig) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#condition-v1-meta) array_ | Conditions represent the latest available observations of the MCPExternalAuthConfig's state | | Optional: \{\}
| +| `observedGeneration` _integer_ | ObservedGeneration is the most recent generation observed for this MCPExternalAuthConfig.
It corresponds to the MCPExternalAuthConfig's generation, which is updated on mutation by the API Server. | | Optional: \{\}
| +| `configHash` _string_ | ConfigHash is a hash of the current configuration for change detection | | Optional: \{\}
| +| `referencingServers` _string array_ | ReferencingServers is a list of MCPServer resources that reference this MCPExternalAuthConfig
This helps track which servers need to be reconciled when this config changes | | Optional: \{\}
| + +#### api.v1alpha1.MCPGroup + +MCPGroup is the Schema for the mcpgroups API + +_Appears in:_ + +- [api.v1alpha1.MCPGroupList](#apiv1alpha1mcpgrouplist) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `MCPGroup` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `spec` _[api.v1alpha1.MCPGroupSpec](#apiv1alpha1mcpgroupspec)_ | | | | +| `status` _[api.v1alpha1.MCPGroupStatus](#apiv1alpha1mcpgroupstatus)_ | | | | + +#### api.v1alpha1.MCPGroupList + +MCPGroupList contains a list of MCPGroup + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `MCPGroupList` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `items` _[api.v1alpha1.MCPGroup](#apiv1alpha1mcpgroup) array_ | | | | + +#### api.v1alpha1.MCPGroupPhase + +_Underlying type:_ _string_ + +MCPGroupPhase represents the lifecycle phase of an MCPGroup + +_Validation:_ + +- Enum: [Ready Pending Failed] + +_Appears in:_ + +- [api.v1alpha1.MCPGroupStatus](#apiv1alpha1mcpgroupstatus) + +| Field | Description | +| --------- | ------------------------------------------------------------ | +| `Ready` | MCPGroupPhaseReady indicates the MCPGroup is ready
| +| `Pending` | MCPGroupPhasePending indicates the MCPGroup is pending
| +| `Failed` | MCPGroupPhaseFailed indicates the MCPGroup has failed
| + +#### api.v1alpha1.MCPGroupSpec + +MCPGroupSpec defines the desired state of MCPGroup + +_Appears in:_ + +- [api.v1alpha1.MCPGroup](#apiv1alpha1mcpgroup) + +| Field | Description | Default | Validation | +| ---------------------- | ------------------------------------------- | ------- | --------------------- | +| `description` _string_ | Description provides human-readable context | | Optional: \{\}
| + +#### api.v1alpha1.MCPGroupStatus + +MCPGroupStatus defines observed state + +_Appears in:_ + +- [api.v1alpha1.MCPGroup](#apiv1alpha1mcpgroup) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------ | ------- | -------------------------------------------------------- | +| `phase` _[api.v1alpha1.MCPGroupPhase](#apiv1alpha1mcpgroupphase)_ | Phase indicates current state | Pending | Enum: [Ready Pending Failed]
Optional: \{\}
| +| `servers` _string array_ | Servers lists MCPServer names in this group | | Optional: \{\}
| +| `serverCount` _integer_ | ServerCount is the number of MCPServers | | Optional: \{\}
| +| `remoteProxies` _string array_ | RemoteProxies lists MCPRemoteProxy names in this group | | Optional: \{\}
| +| `remoteProxyCount` _integer_ | RemoteProxyCount is the number of MCPRemoteProxies | | Optional: \{\}
| +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#condition-v1-meta) array_ | Conditions represent observations | | Optional: \{\}
| + +#### api.v1alpha1.MCPRegistry + +MCPRegistry is the Schema for the mcpregistries API + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryList](#apiv1alpha1mcpregistrylist) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `MCPRegistry` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `spec` _[api.v1alpha1.MCPRegistrySpec](#apiv1alpha1mcpregistryspec)_ | | | | +| `status` _[api.v1alpha1.MCPRegistryStatus](#apiv1alpha1mcpregistrystatus)_ | | | | + +#### api.v1alpha1.MCPRegistryAuthConfig + +MCPRegistryAuthConfig defines authentication configuration for the registry API server. + +_Appears in:_ + +- [api.v1alpha1.MCPRegistrySpec](#apiv1alpha1mcpregistryspec) + +| Field | Description | Default | Validation | +| ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | --------------------------------------------------- | +| `mode` _[api.v1alpha1.MCPRegistryAuthMode](#apiv1alpha1mcpregistryauthmode)_ | Mode specifies the authentication mode (anonymous or oauth)
Defaults to "anonymous" if not specified.
Use "oauth" to enable OAuth/OIDC authentication. | anonymous | Enum: [anonymous oauth]
Optional: \{\}
| +| `oauth` _[api.v1alpha1.MCPRegistryOAuthConfig](#apiv1alpha1mcpregistryoauthconfig)_ | OAuth defines OAuth/OIDC specific authentication settings
Only used when Mode is "oauth" | | Optional: \{\}
| + +#### api.v1alpha1.MCPRegistryAuthMode + +_Underlying type:_ _string_ + +MCPRegistryAuthMode represents the authentication mode for the registry API server + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryAuthConfig](#apiv1alpha1mcpregistryauthconfig) + +| Field | Description | +| ----------- | ---------------------------------------------------------------- | +| `anonymous` | MCPRegistryAuthModeAnonymous allows unauthenticated access
| +| `oauth` | MCPRegistryAuthModeOAuth enables OAuth/OIDC authentication
| + +#### api.v1alpha1.MCPRegistryConfig + +MCPRegistryConfig defines the configuration for a registry data source + +_Appears in:_ + +- [api.v1alpha1.MCPRegistrySpec](#apiv1alpha1mcpregistryspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ---------------------------------------- | +| `name` _string_ | Name is a unique identifier for this registry configuration within the MCPRegistry | | MinLength: 1
Required: \{\}
| +| `format` _string_ | Format is the data format (toolhive, upstream) | toolhive | Enum: [toolhive upstream]
| +| `configMapRef` _[ConfigMapKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#configmapkeyselector-v1-core)_ | ConfigMapRef defines the ConfigMap source configuration
Mutually exclusive with Git, API, and PVCRef | | Optional: \{\}
| +| `git` _[api.v1alpha1.GitSource](#apiv1alpha1gitsource)_ | Git defines the Git repository source configuration
Mutually exclusive with ConfigMapRef, API, and PVCRef | | Optional: \{\}
| +| `api` _[api.v1alpha1.APISource](#apiv1alpha1apisource)_ | API defines the API source configuration
Mutually exclusive with ConfigMapRef, Git, and PVCRef | | Optional: \{\}
| +| `pvcRef` _[api.v1alpha1.PVCSource](#apiv1alpha1pvcsource)_ | PVCRef defines the PersistentVolumeClaim source configuration
Mutually exclusive with ConfigMapRef, Git, and API | | Optional: \{\}
| +| `syncPolicy` _[api.v1alpha1.SyncPolicy](#apiv1alpha1syncpolicy)_ | SyncPolicy defines the automatic synchronization behavior for this registry.
If specified, enables automatic synchronization at the given interval.
Manual synchronization is always supported via annotation-based triggers
regardless of this setting. | | Optional: \{\}
| +| `filter` _[api.v1alpha1.RegistryFilter](#apiv1alpha1registryfilter)_ | Filter defines include/exclude patterns for registry content | | Optional: \{\}
| + +#### api.v1alpha1.MCPRegistryDatabaseConfig + +MCPRegistryDatabaseConfig defines PostgreSQL database configuration for the registry API server. +Uses a two-user security model: separate users for operations and migrations. + +_Appears in:_ + +- [api.v1alpha1.MCPRegistrySpec](#apiv1alpha1mcpregistryspec) + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | -------------------------------------------------------------------------------------- | +| `host` _string_ | Host is the database server hostname | postgres | Optional: \{\}
| +| `port` _integer_ | Port is the database server port | 5432 | Maximum: 65535
Minimum: 1
Optional: \{\}
| +| `user` _string_ | User is the application user (limited privileges: SELECT, INSERT, UPDATE, DELETE)
Credentials should be provided via pgpass file or environment variables | db_app | Optional: \{\}
| +| `migrationUser` _string_ | MigrationUser is the migration user (elevated privileges: CREATE, ALTER, DROP)
Used for running database schema migrations
Credentials should be provided via pgpass file or environment variables | db_migrator | Optional: \{\}
| +| `database` _string_ | Database is the database name | registry | Optional: \{\}
| +| `sslMode` _string_ | SSLMode is the SSL mode for the connection
Valid values: disable, allow, prefer, require, verify-ca, verify-full | prefer | Enum: [disable allow prefer require verify-ca verify-full]
Optional: \{\}
| +| `maxOpenConns` _integer_ | MaxOpenConns is the maximum number of open connections to the database | 10 | Minimum: 1
Optional: \{\}
| +| `maxIdleConns` _integer_ | MaxIdleConns is the maximum number of idle connections in the pool | 2 | Minimum: 0
Optional: \{\}
| +| `connMaxLifetime` _string_ | ConnMaxLifetime is the maximum amount of time a connection may be reused (Go duration format)
Examples: "30m", "1h", "24h" | 30m | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Optional: \{\}
| +| `dbAppUserPasswordSecretRef` _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#secretkeyselector-v1-core)_ | DBAppUserPasswordSecretRef references a Kubernetes Secret containing the password for the application database user.
The operator will use this password along with DBMigrationUserPasswordSecretRef to generate a pgpass file
that is mounted to the registry API container. | | Required: \{\}
| +| `dbMigrationUserPasswordSecretRef` _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#secretkeyselector-v1-core)_ | DBMigrationUserPasswordSecretRef references a Kubernetes Secret containing the password for the migration database user.
The operator will use this password along with DBAppUserPasswordSecretRef to generate a pgpass file
that is mounted to the registry API container. | | Required: \{\}
| + +#### api.v1alpha1.MCPRegistryList + +MCPRegistryList contains a list of MCPRegistry + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `MCPRegistryList` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `items` _[api.v1alpha1.MCPRegistry](#apiv1alpha1mcpregistry) array_ | | | | + +#### api.v1alpha1.MCPRegistryOAuthConfig + +MCPRegistryOAuthConfig defines OAuth/OIDC specific authentication settings + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryAuthConfig](#apiv1alpha1mcpregistryauthconfig) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | --------------------------------------- | +| `resourceUrl` _string_ | ResourceURL is the URL identifying this protected resource (RFC 9728)
Used in the /.well-known/oauth-protected-resource endpoint | | Optional: \{\}
| +| `providers` _[api.v1alpha1.MCPRegistryOAuthProviderConfig](#apiv1alpha1mcpregistryoauthproviderconfig) array_ | Providers defines the OAuth/OIDC providers for authentication
Multiple providers can be configured (e.g., Kubernetes + external IDP) | | MinItems: 1
Optional: \{\}
| +| `scopesSupported` _string array_ | ScopesSupported defines the OAuth scopes supported by this resource (RFC 9728)
Defaults to ["mcp-registry:read", "mcp-registry:write"] if not specified | | Optional: \{\}
| +| `realm` _string_ | Realm is the protection space identifier for WWW-Authenticate header (RFC 7235)
Defaults to "mcp-registry" if not specified | | Optional: \{\}
| + +#### api.v1alpha1.MCPRegistryOAuthProviderConfig + +MCPRegistryOAuthProviderConfig defines configuration for an OAuth/OIDC provider + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryOAuthConfig](#apiv1alpha1mcpregistryoauthconfig) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------------- | +| `name` _string_ | Name is a unique identifier for this provider (e.g., "kubernetes", "keycloak") | | MinLength: 1
Required: \{\}
| +| `issuerUrl` _string_ | IssuerURL is the OIDC issuer URL (e.g., https://accounts.google.com)
The JWKS URL will be discovered automatically from .well-known/openid-configuration
unless JwksUrl is explicitly specified | | MinLength: 1
Pattern: `^https?://.*`
Required: \{\}
| +| `jwksUrl` _string_ | JwksUrl is the URL to fetch the JSON Web Key Set (JWKS) from
If specified, OIDC discovery is skipped and this URL is used directly
Example: https://kubernetes.default.svc/openid/v1/jwks | | Pattern: `^https?://.*`
Optional: \{\}
| +| `audience` _string_ | Audience is the expected audience claim in the token (REQUIRED)
Per RFC 6749 Section 4.1.3, tokens must be validated against expected audience
For Kubernetes, this is typically the API server URL | | MinLength: 1
Required: \{\}
| +| `clientId` _string_ | ClientID is the OAuth client ID for token introspection (optional) | | Optional: \{\}
| +| `clientSecretRef` _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#secretkeyselector-v1-core)_ | ClientSecretRef is a reference to a Secret containing the client secret
The secret should have a key "clientSecret" containing the secret value | | Optional: \{\}
| +| `caCertRef` _[ConfigMapKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#configmapkeyselector-v1-core)_ | CACertRef is a reference to a ConfigMap containing the CA certificate bundle
for verifying the provider's TLS certificate.
Required for Kubernetes in-cluster authentication or self-signed certificates | | Optional: \{\}
| +| `caCertPath` _string_ | CaCertPath is the path to the CA certificate bundle for verifying the provider's TLS certificate.
Required for Kubernetes in-cluster authentication or self-signed certificates | | Optional: \{\}
| +| `authTokenRef` _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#secretkeyselector-v1-core)_ | AuthTokenRef is a reference to a Secret containing a bearer token for authenticating
to OIDC/JWKS endpoints. Useful when the OIDC discovery or JWKS endpoint requires authentication.
Example: ServiceAccount token for Kubernetes API server | | Optional: \{\}
| +| `authTokenFile` _string_ | AuthTokenFile is the path to a file containing a bearer token for authenticating to OIDC/JWKS endpoints.
Useful when the OIDC discovery or JWKS endpoint requires authentication.
Example: /var/run/secrets/kubernetes.io/serviceaccount/token | | Optional: \{\}
| +| `introspectionUrl` _string_ | IntrospectionURL is the OAuth 2.0 Token Introspection endpoint (RFC 7662)
Used for validating opaque (non-JWT) tokens
If not specified, only JWT tokens can be validated via JWKS | | Pattern: `^https?://.*`
Optional: \{\}
| +| `allowPrivateIP` _boolean_ | AllowPrivateIP allows JWKS/OIDC endpoints on private IP addresses
Required when the OAuth provider (e.g., Kubernetes API server) is running on a private network
Example: Set to true when using https://kubernetes.default.svc as the issuer URL | false | Optional: \{\}
| + +#### api.v1alpha1.MCPRegistryPhase + +_Underlying type:_ _string_ + +MCPRegistryPhase represents the phase of the MCPRegistry + +_Validation:_ + +- Enum: [Pending Ready Failed Syncing Terminating] + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryStatus](#apiv1alpha1mcpregistrystatus) + +| Field | Description | +| ------------- | ----------------------------------------------------------------------------- | +| `Pending` | MCPRegistryPhasePending means the MCPRegistry is being initialized
| +| `Ready` | MCPRegistryPhaseReady means the MCPRegistry is ready and operational
| +| `Failed` | MCPRegistryPhaseFailed means the MCPRegistry has failed
| +| `Syncing` | MCPRegistryPhaseSyncing means the MCPRegistry is currently syncing data
| +| `Terminating` | MCPRegistryPhaseTerminating means the MCPRegistry is being deleted
| + +#### api.v1alpha1.MCPRegistrySpec + +MCPRegistrySpec defines the desired state of MCPRegistry + +_Appears in:_ + +- [api.v1alpha1.MCPRegistry](#apiv1alpha1mcpregistry) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------- | +| `displayName` _string_ | DisplayName is a human-readable name for the registry | | Optional: \{\}
| +| `registries` _[api.v1alpha1.MCPRegistryConfig](#apiv1alpha1mcpregistryconfig) array_ | Registries defines the configuration for the registry data sources | | MinItems: 1
Required: \{\}
| +| `enforceServers` _boolean_ | EnforceServers indicates whether MCPServers in this namespace must have their images
present in at least one registry in the namespace. When any registry in the namespace
has this field set to true, enforcement is enabled for the entire namespace.
MCPServers with images not found in any registry will be rejected.
When false (default), MCPServers can be deployed regardless of registry presence. | false | Optional: \{\}
| +| `podTemplateSpec` _[RawExtension](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#rawextension-runtime-pkg)_ | PodTemplateSpec defines the pod template to use for the registry API server
This allows for customizing the pod configuration beyond what is provided by the other fields.
Note that to modify the specific container the registry API server runs in, you must specify
the `registry-api` container name in the PodTemplateSpec.
This field accepts a PodTemplateSpec object as JSON/YAML. | | Type: object
Optional: \{\}
| +| `databaseConfig` _[api.v1alpha1.MCPRegistryDatabaseConfig](#apiv1alpha1mcpregistrydatabaseconfig)_ | DatabaseConfig defines the PostgreSQL database configuration for the registry API server.
If not specified, defaults will be used:
- Host: "postgres"
- Port: 5432
- User: "db_app"
- MigrationUser: "db_migrator"
- Database: "registry"
- SSLMode: "prefer"
- MaxOpenConns: 10
- MaxIdleConns: 2
- ConnMaxLifetime: "30m" | | Optional: \{\}
| +| `authConfig` _[api.v1alpha1.MCPRegistryAuthConfig](#apiv1alpha1mcpregistryauthconfig)_ | AuthConfig defines the authentication configuration for the registry API server.
If not specified, defaults to anonymous authentication. | | Optional: \{\}
| + +#### api.v1alpha1.MCPRegistryStatus + +MCPRegistryStatus defines the observed state of MCPRegistry + +_Appears in:_ + +- [api.v1alpha1.MCPRegistry](#apiv1alpha1mcpregistry) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------------------- | +| `phase` _[api.v1alpha1.MCPRegistryPhase](#apiv1alpha1mcpregistryphase)_ | Phase represents the current overall phase of the MCPRegistry
Derived from sync and API status | | Enum: [Pending Ready Failed Syncing Terminating]
Optional: \{\}
| +| `message` _string_ | Message provides additional information about the current phase | | Optional: \{\}
| +| `syncStatus` _[api.v1alpha1.SyncStatus](#apiv1alpha1syncstatus)_ | SyncStatus provides detailed information about data synchronization | | Optional: \{\}
| +| `apiStatus` _[api.v1alpha1.APIStatus](#apiv1alpha1apistatus)_ | APIStatus provides detailed information about the API service | | Optional: \{\}
| +| `lastAppliedFilterHash` _string_ | LastAppliedFilterHash is the hash of the last applied filter | | Optional: \{\}
| +| `storageRef` _[api.v1alpha1.StorageReference](#apiv1alpha1storagereference)_ | StorageRef is a reference to the internal storage location | | Optional: \{\}
| +| `lastManualSyncTrigger` _string_ | LastManualSyncTrigger tracks the last processed manual sync annotation value
Used to detect new manual sync requests via toolhive.stacklok.dev/sync-trigger annotation | | Optional: \{\}
| +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#condition-v1-meta) array_ | Conditions represent the latest available observations of the MCPRegistry's state | | Optional: \{\}
| + +#### api.v1alpha1.MCPRemoteProxy + +MCPRemoteProxy is the Schema for the mcpremoteproxies API +It enables proxying remote MCP servers with authentication, authorization, audit logging, and tool filtering + +_Appears in:_ + +- [api.v1alpha1.MCPRemoteProxyList](#apiv1alpha1mcpremoteproxylist) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `MCPRemoteProxy` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `spec` _[api.v1alpha1.MCPRemoteProxySpec](#apiv1alpha1mcpremoteproxyspec)_ | | | | +| `status` _[api.v1alpha1.MCPRemoteProxyStatus](#apiv1alpha1mcpremoteproxystatus)_ | | | | + +#### api.v1alpha1.MCPRemoteProxyList + +MCPRemoteProxyList contains a list of MCPRemoteProxy + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `MCPRemoteProxyList` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `items` _[api.v1alpha1.MCPRemoteProxy](#apiv1alpha1mcpremoteproxy) array_ | | | | + +#### api.v1alpha1.MCPRemoteProxyPhase + +_Underlying type:_ _string_ + +MCPRemoteProxyPhase is a label for the condition of a MCPRemoteProxy at the current time + +_Validation:_ + +- Enum: [Pending Ready Failed Terminating] + +_Appears in:_ + +- [api.v1alpha1.MCPRemoteProxyStatus](#apiv1alpha1mcpremoteproxystatus) + +| Field | Description | +| ------------- | --------------------------------------------------------------------------------------- | +| `Pending` | MCPRemoteProxyPhasePending means the proxy is being created
| +| `Ready` | MCPRemoteProxyPhaseReady means the proxy is ready and operational
| +| `Failed` | MCPRemoteProxyPhaseFailed means the proxy failed to start or encountered an error
| +| `Terminating` | MCPRemoteProxyPhaseTerminating means the proxy is being deleted
| + +#### api.v1alpha1.MCPRemoteProxySpec + +MCPRemoteProxySpec defines the desired state of MCPRemoteProxy + +_Appears in:_ + +- [api.v1alpha1.MCPRemoteProxy](#apiv1alpha1mcpremoteproxy) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | ------------------------------------------------- | +| `remoteURL` _string_ | RemoteURL is the URL of the remote MCP server to proxy | | Pattern: `^https?://`
Required: \{\}
| +| `port` _integer_ | Port is the port to expose the MCP proxy on
Deprecated: Use ProxyPort instead | 8080 | Maximum: 65535
Minimum: 1
| +| `proxyPort` _integer_ | ProxyPort is the port to expose the MCP proxy on | 8080 | Maximum: 65535
Minimum: 1
| +| `transport` _string_ | Transport is the transport method for the remote proxy (sse or streamable-http) | streamable-http | Enum: [sse streamable-http]
| +| `oidcConfig` _[api.v1alpha1.OIDCConfigRef](#apiv1alpha1oidcconfigref)_ | OIDCConfig defines OIDC authentication configuration for the proxy
This validates incoming tokens from clients. Required for proxy mode. | | Required: \{\}
| +| `externalAuthConfigRef` _[api.v1alpha1.ExternalAuthConfigRef](#apiv1alpha1externalauthconfigref)_ | ExternalAuthConfigRef references a MCPExternalAuthConfig resource for token exchange.
When specified, the proxy will exchange validated incoming tokens for remote service tokens.
The referenced MCPExternalAuthConfig must exist in the same namespace as this MCPRemoteProxy. | | Optional: \{\}
| +| `headerForward` _[api.v1alpha1.HeaderForwardConfig](#apiv1alpha1headerforwardconfig)_ | HeaderForward configures headers to inject into requests to the remote MCP server.
Use this to add custom headers like X-Tenant-ID or correlation IDs. | | Optional: \{\}
| +| `authzConfig` _[api.v1alpha1.AuthzConfigRef](#apiv1alpha1authzconfigref)_ | AuthzConfig defines authorization policy configuration for the proxy | | Optional: \{\}
| +| `audit` _[api.v1alpha1.AuditConfig](#apiv1alpha1auditconfig)_ | Audit defines audit logging configuration for the proxy | | Optional: \{\}
| +| `toolConfigRef` _[api.v1alpha1.ToolConfigRef](#apiv1alpha1toolconfigref)_ | ToolConfigRef references a MCPToolConfig resource for tool filtering and renaming.
The referenced MCPToolConfig must exist in the same namespace as this MCPRemoteProxy.
Cross-namespace references are not supported for security and isolation reasons.
If specified, this allows filtering and overriding tools from the remote MCP server. | | Optional: \{\}
| +| `telemetry` _[api.v1alpha1.TelemetryConfig](#apiv1alpha1telemetryconfig)_ | Telemetry defines observability configuration for the proxy | | Optional: \{\}
| +| `resources` _[api.v1alpha1.ResourceRequirements](#apiv1alpha1resourcerequirements)_ | Resources defines the resource requirements for the proxy container | | Optional: \{\}
| +| `serviceAccount` _string_ | ServiceAccount is the name of an already existing service account to use by the proxy.
If not specified, a ServiceAccount will be created automatically and used by the proxy. | | Optional: \{\}
| +| `trustProxyHeaders` _boolean_ | TrustProxyHeaders indicates whether to trust X-Forwarded-\* headers from reverse proxies
When enabled, the proxy will use X-Forwarded-Proto, X-Forwarded-Host, X-Forwarded-Port,
and X-Forwarded-Prefix headers to construct endpoint URLs | false | Optional: \{\}
| +| `endpointPrefix` _string_ | EndpointPrefix is the path prefix to prepend to SSE endpoint URLs.
This is used to handle path-based ingress routing scenarios where the ingress
strips a path prefix before forwarding to the backend. | | Optional: \{\}
| +| `resourceOverrides` _[api.v1alpha1.ResourceOverrides](#apiv1alpha1resourceoverrides)_ | ResourceOverrides allows overriding annotations and labels for resources created by the operator | | Optional: \{\}
| +| `groupRef` _string_ | GroupRef is the name of the MCPGroup this proxy belongs to
Must reference an existing MCPGroup in the same namespace | | Optional: \{\}
| +| `sessionAffinity` _string_ | SessionAffinity controls whether the Service routes repeated client connections to the same pod.
MCP protocols (SSE, streamable-http) are stateful, so ClientIP is the default.
Set to "None" for stateless servers or when using an external load balancer with its own affinity. | ClientIP | Enum: [ClientIP None]
Optional: \{\}
| + +#### api.v1alpha1.MCPRemoteProxyStatus + +MCPRemoteProxyStatus defines the observed state of MCPRemoteProxy + +_Appears in:_ + +- [api.v1alpha1.MCPRemoteProxy](#apiv1alpha1mcpremoteproxy) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- | ------- | -------------------------------------------------------------------- | +| `phase` _[api.v1alpha1.MCPRemoteProxyPhase](#apiv1alpha1mcpremoteproxyphase)_ | Phase is the current phase of the MCPRemoteProxy | | Enum: [Pending Ready Failed Terminating]
Optional: \{\}
| +| `url` _string_ | URL is the internal cluster URL where the proxy can be accessed | | Optional: \{\}
| +| `externalURL` _string_ | ExternalURL is the external URL where the proxy can be accessed (if exposed externally) | | Optional: \{\}
| +| `observedGeneration` _integer_ | ObservedGeneration reflects the generation of the most recently observed MCPRemoteProxy | | Optional: \{\}
| +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#condition-v1-meta) array_ | Conditions represent the latest available observations of the MCPRemoteProxy's state | | Optional: \{\}
| +| `toolConfigHash` _string_ | ToolConfigHash stores the hash of the referenced ToolConfig for change detection | | Optional: \{\}
| +| `externalAuthConfigHash` _string_ | ExternalAuthConfigHash is the hash of the referenced MCPExternalAuthConfig spec | | Optional: \{\}
| +| `message` _string_ | Message provides additional information about the current phase | | Optional: \{\}
| + +#### api.v1alpha1.MCPServer + +MCPServer is the Schema for the mcpservers API + +_Appears in:_ + +- [api.v1alpha1.MCPServerList](#apiv1alpha1mcpserverlist) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `MCPServer` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `spec` _[api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec)_ | | | | +| `status` _[api.v1alpha1.MCPServerStatus](#apiv1alpha1mcpserverstatus)_ | | | | + +#### api.v1alpha1.MCPServerList + +MCPServerList contains a list of MCPServer + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `MCPServerList` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `items` _[api.v1alpha1.MCPServer](#apiv1alpha1mcpserver) array_ | | | | + +#### api.v1alpha1.MCPServerPhase + +_Underlying type:_ _string_ + +MCPServerPhase is the phase of the MCPServer + +_Validation:_ + +- Enum: [Pending Running Failed Terminating Stopped] + +_Appears in:_ + +- [api.v1alpha1.MCPServerStatus](#apiv1alpha1mcpserverstatus) + +| Field | Description | +| ------------- | -------------------------------------------------------------------- | +| `Pending` | MCPServerPhasePending means the MCPServer is being created
| +| `Running` | MCPServerPhaseRunning means the MCPServer is running
| +| `Failed` | MCPServerPhaseFailed means the MCPServer failed to start
| +| `Terminating` | MCPServerPhaseTerminating means the MCPServer is being deleted
| +| `Stopped` | MCPServerPhaseStopped means the MCPServer is scaled to zero
| + +#### api.v1alpha1.MCPServerSpec + +MCPServerSpec defines the desired state of MCPServer + +_Appears in:_ + +- [api.v1alpha1.MCPServer](#apiv1alpha1mcpserver) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------------- | ----------------------------------------------------------- | +| `image` _string_ | Image is the container image for the MCP server | | Required: \{\}
| +| `transport` _string_ | Transport is the transport method for the MCP server (stdio, streamable-http or sse) | stdio | Enum: [stdio streamable-http sse]
| +| `proxyMode` _string_ | ProxyMode is the proxy mode for stdio transport (sse or streamable-http)
This setting is ONLY applicable when Transport is "stdio".
For direct transports (sse, streamable-http), this field is ignored.
The default value is applied by Kubernetes but will be ignored for non-stdio transports. | streamable-http | Enum: [sse streamable-http]
Optional: \{\}
| +| `port` _integer_ | Port is the port to expose the MCP server on
Deprecated: Use ProxyPort instead | 8080 | Maximum: 65535
Minimum: 1
| +| `targetPort` _integer_ | TargetPort is the port that MCP server listens to
Deprecated: Use McpPort instead | | Maximum: 65535
Minimum: 1
Optional: \{\}
| +| `proxyPort` _integer_ | ProxyPort is the port to expose the proxy runner on | 8080 | Maximum: 65535
Minimum: 1
| +| `mcpPort` _integer_ | McpPort is the port that MCP server listens to | | Maximum: 65535
Minimum: 1
Optional: \{\}
| +| `args` _string array_ | Args are additional arguments to pass to the MCP server | | Optional: \{\}
| +| `env` _[api.v1alpha1.EnvVar](#apiv1alpha1envvar) array_ | Env are environment variables to set in the MCP server container | | Optional: \{\}
| +| `volumes` _[api.v1alpha1.Volume](#apiv1alpha1volume) array_ | Volumes are volumes to mount in the MCP server container | | Optional: \{\}
| +| `resources` _[api.v1alpha1.ResourceRequirements](#apiv1alpha1resourcerequirements)_ | Resources defines the resource requirements for the MCP server container | | Optional: \{\}
| +| `secrets` _[api.v1alpha1.SecretRef](#apiv1alpha1secretref) array_ | Secrets are references to secrets to mount in the MCP server container | | Optional: \{\}
| +| `serviceAccount` _string_ | ServiceAccount is the name of an already existing service account to use by the MCP server.
If not specified, a ServiceAccount will be created automatically and used by the MCP server. | | Optional: \{\}
| +| `permissionProfile` _[api.v1alpha1.PermissionProfileRef](#apiv1alpha1permissionprofileref)_ | PermissionProfile defines the permission profile to use | | Optional: \{\}
| +| `podTemplateSpec` _[RawExtension](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#rawextension-runtime-pkg)_ | PodTemplateSpec defines the pod template to use for the MCP server
This allows for customizing the pod configuration beyond what is provided by the other fields.
Note that to modify the specific container the MCP server runs in, you must specify
the `mcp` container name in the PodTemplateSpec.
This field accepts a PodTemplateSpec object as JSON/YAML. | | Type: object
Optional: \{\}
| +| `resourceOverrides` _[api.v1alpha1.ResourceOverrides](#apiv1alpha1resourceoverrides)_ | ResourceOverrides allows overriding annotations and labels for resources created by the operator | | Optional: \{\}
| +| `oidcConfig` _[api.v1alpha1.OIDCConfigRef](#apiv1alpha1oidcconfigref)_ | OIDCConfig defines OIDC authentication configuration for the MCP server | | Optional: \{\}
| +| `authzConfig` _[api.v1alpha1.AuthzConfigRef](#apiv1alpha1authzconfigref)_ | AuthzConfig defines authorization policy configuration for the MCP server | | Optional: \{\}
| +| `audit` _[api.v1alpha1.AuditConfig](#apiv1alpha1auditconfig)_ | Audit defines audit logging configuration for the MCP server | | Optional: \{\}
| +| `tools` _string array_ | ToolsFilter is the filter on tools applied to the MCP server
Deprecated: Use ToolConfigRef instead | | Optional: \{\}
| +| `toolConfigRef` _[api.v1alpha1.ToolConfigRef](#apiv1alpha1toolconfigref)_ | ToolConfigRef references a MCPToolConfig resource for tool filtering and renaming.
The referenced MCPToolConfig must exist in the same namespace as this MCPServer.
Cross-namespace references are not supported for security and isolation reasons.
If specified, this takes precedence over the inline ToolsFilter field. | | Optional: \{\}
| +| `externalAuthConfigRef` _[api.v1alpha1.ExternalAuthConfigRef](#apiv1alpha1externalauthconfigref)_ | ExternalAuthConfigRef references a MCPExternalAuthConfig resource for external authentication.
The referenced MCPExternalAuthConfig must exist in the same namespace as this MCPServer. | | Optional: \{\}
| +| `telemetry` _[api.v1alpha1.TelemetryConfig](#apiv1alpha1telemetryconfig)_ | Telemetry defines observability configuration for the MCP server | | Optional: \{\}
| +| `trustProxyHeaders` _boolean_ | TrustProxyHeaders indicates whether to trust X-Forwarded-\* headers from reverse proxies
When enabled, the proxy will use X-Forwarded-Proto, X-Forwarded-Host, X-Forwarded-Port,
and X-Forwarded-Prefix headers to construct endpoint URLs | false | Optional: \{\}
| +| `endpointPrefix` _string_ | EndpointPrefix is the path prefix to prepend to SSE endpoint URLs.
This is used to handle path-based ingress routing scenarios where the ingress
strips a path prefix before forwarding to the backend. | | Optional: \{\}
| +| `groupRef` _string_ | GroupRef is the name of the MCPGroup this server belongs to
Must reference an existing MCPGroup in the same namespace | | Optional: \{\}
| +| `sessionAffinity` _string_ | SessionAffinity controls whether the Service routes repeated client connections to the same pod.
MCP protocols (SSE, streamable-http) are stateful, so ClientIP is the default.
Set to "None" for stateless servers or when using an external load balancer with its own affinity. | ClientIP | Enum: [ClientIP None]
Optional: \{\}
| + +#### api.v1alpha1.MCPServerStatus + +MCPServerStatus defines the observed state of MCPServer + +_Appears in:_ + +- [api.v1alpha1.MCPServer](#apiv1alpha1mcpserver) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------ | +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#condition-v1-meta) array_ | Conditions represent the latest available observations of the MCPServer's state | | Optional: \{\}
| +| `toolConfigHash` _string_ | ToolConfigHash stores the hash of the referenced ToolConfig for change detection | | Optional: \{\}
| +| `externalAuthConfigHash` _string_ | ExternalAuthConfigHash is the hash of the referenced MCPExternalAuthConfig spec | | Optional: \{\}
| +| `url` _string_ | URL is the URL where the MCP server can be accessed | | Optional: \{\}
| +| `phase` _[api.v1alpha1.MCPServerPhase](#apiv1alpha1mcpserverphase)_ | Phase is the current phase of the MCPServer | | Enum: [Pending Running Failed Terminating Stopped]
Optional: \{\}
| +| `message` _string_ | Message provides additional information about the current phase | | Optional: \{\}
| +| `readyReplicas` _integer_ | ReadyReplicas is the number of ready proxy replicas | | Optional: \{\}
| + +#### api.v1alpha1.MCPToolConfig + +MCPToolConfig is the Schema for the mcptoolconfigs API. +MCPToolConfig resources are namespace-scoped and can only be referenced by +MCPServer resources within the same namespace. Cross-namespace references +are not supported for security and isolation reasons. + +_Appears in:_ + +- [api.v1alpha1.MCPToolConfigList](#apiv1alpha1mcptoolconfiglist) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `MCPToolConfig` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `spec` _[api.v1alpha1.MCPToolConfigSpec](#apiv1alpha1mcptoolconfigspec)_ | | | | +| `status` _[api.v1alpha1.MCPToolConfigStatus](#apiv1alpha1mcptoolconfigstatus)_ | | | | + +#### api.v1alpha1.MCPToolConfigList + +MCPToolConfigList contains a list of MCPToolConfig + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `MCPToolConfigList` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `items` _[api.v1alpha1.MCPToolConfig](#apiv1alpha1mcptoolconfig) array_ | | | | + +#### api.v1alpha1.MCPToolConfigSpec + +MCPToolConfigSpec defines the desired state of MCPToolConfig. +MCPToolConfig resources are namespace-scoped and can only be referenced by +MCPServer resources in the same namespace. + +_Appears in:_ + +- [api.v1alpha1.MCPToolConfig](#apiv1alpha1mcptoolconfig) + +| Field | Description | Default | Validation | +| ---------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `toolsFilter` _string array_ | ToolsFilter is a list of tool names to filter (allow list).
Only tools in this list will be exposed by the MCP server.
If empty, all tools are exposed. | | Optional: \{\}
| +| `toolsOverride` _object (keys:string, values:[api.v1alpha1.ToolOverride](#apiv1alpha1tooloverride))_ | ToolsOverride is a map from actual tool names to their overridden configuration.
This allows renaming tools and/or changing their descriptions. | | Optional: \{\}
| + +#### api.v1alpha1.MCPToolConfigStatus + +MCPToolConfigStatus defines the observed state of MCPToolConfig + +_Appears in:_ + +- [api.v1alpha1.MCPToolConfig](#apiv1alpha1mcptoolconfig) + +| Field | Description | Default | Validation | +| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `observedGeneration` _integer_ | ObservedGeneration is the most recent generation observed for this MCPToolConfig.
It corresponds to the MCPToolConfig's generation, which is updated on mutation by the API Server. | | Optional: \{\}
| +| `configHash` _string_ | ConfigHash is a hash of the current configuration for change detection | | Optional: \{\}
| +| `referencingServers` _string array_ | ReferencingServers is a list of MCPServer resources that reference this MCPToolConfig
This helps track which servers need to be reconciled when this config changes | | Optional: \{\}
| + +#### api.v1alpha1.ModelCacheConfig + +ModelCacheConfig configures persistent storage for model caching + +_Appears in:_ + +- [api.v1alpha1.EmbeddingServerSpec](#apiv1alpha1embeddingserverspec) + +| Field | Description | Default | Validation | +| --------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ------------- | ---------------------------------------------------------------------------- | +| `enabled` _boolean_ | Enabled controls whether model caching is enabled | true | Optional: \{\}
| +| `storageClassName` _string_ | StorageClassName is the storage class to use for the PVC
If not specified, uses the cluster's default storage class | | Optional: \{\}
| +| `size` _string_ | Size is the size of the PVC for model caching (e.g., "10Gi") | 10Gi | Optional: \{\}
| +| `accessMode` _string_ | AccessMode is the access mode for the PVC | ReadWriteOnce | Enum: [ReadWriteOnce ReadWriteMany ReadOnlyMany]
Optional: \{\}
| + +#### api.v1alpha1.NameFilter + +NameFilter defines name-based filtering + +_Appears in:_ + +- [api.v1alpha1.RegistryFilter](#apiv1alpha1registryfilter) + +| Field | Description | Default | Validation | +| ------------------------ | --------------------------------------------- | ------- | --------------------- | +| `include` _string array_ | Include is a list of glob patterns to include | | Optional: \{\}
| +| `exclude` _string array_ | Exclude is a list of glob patterns to exclude | | Optional: \{\}
| + +#### api.v1alpha1.NetworkPermissions + +NetworkPermissions defines the network permissions for an MCP server + +_Appears in:_ + +- [api.v1alpha1.PermissionProfileSpec](#apiv1alpha1permissionprofilespec) + +| Field | Description | Default | Validation | +| ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | --------------------- | +| `mode` _string_ | Mode specifies the network mode for the container (e.g., "host", "bridge", "none")
When empty, the default container runtime network mode is used | | Optional: \{\}
| +| `outbound` _[api.v1alpha1.OutboundNetworkPermissions](#apiv1alpha1outboundnetworkpermissions)_ | Outbound defines the outbound network permissions | | Optional: \{\}
| + +#### api.v1alpha1.OAuth2UpstreamConfig + +OAuth2UpstreamConfig contains configuration for pure OAuth 2.0 providers. +OAuth 2.0 providers require explicit endpoint configuration. + +_Appears in:_ + +- [api.v1alpha1.UpstreamProviderConfig](#apiv1alpha1upstreamproviderconfig) + +| Field | Description | Default | Validation | +| ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------------------- | +| `authorizationEndpoint` _string_ | AuthorizationEndpoint is the URL for the OAuth authorization endpoint. | | Pattern: `^https?://.*$`
Required: \{\}
| +| `tokenEndpoint` _string_ | TokenEndpoint is the URL for the OAuth token endpoint. | | Pattern: `^https?://.*$`
Required: \{\}
| +| `userInfo` _[api.v1alpha1.UserInfoConfig](#apiv1alpha1userinfoconfig)_ | UserInfo contains configuration for fetching user information from the upstream provider.
Required for OAuth2 providers to resolve user identity. | | Required: \{\}
| +| `clientId` _string_ | ClientID is the OAuth 2.0 client identifier registered with the upstream IDP. | | Required: \{\}
| +| `clientSecretRef` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref)_ | ClientSecretRef references a Kubernetes Secret containing the OAuth 2.0 client secret.
Optional for public clients using PKCE instead of client secret. | | Optional: \{\}
| +| `redirectUri` _string_ | RedirectURI is the callback URL where the upstream IDP will redirect after authentication.
When not specified, defaults to `\{resourceUrl\}/oauth/callback` where `resourceUrl` is the
URL associated with the resource (e.g., MCPServer or vMCP) using this config. | | Optional: \{\}
| +| `scopes` _string array_ | Scopes are the OAuth scopes to request from the upstream IDP. | | Optional: \{\}
| +| `tokenResponseMapping` _[api.v1alpha1.TokenResponseMapping](#apiv1alpha1tokenresponsemapping)_ | TokenResponseMapping configures custom field extraction from non-standard token responses.
Some OAuth providers (e.g., GovSlack) nest token fields under non-standard paths
instead of returning them at the top level. When set, ToolHive performs the token
exchange HTTP call directly and extracts fields using the configured dot-notation paths.
If nil, standard OAuth 2.0 token response parsing is used. | | Optional: \{\}
| + +#### api.v1alpha1.OIDCConfigRef + +OIDCConfigRef defines a reference to OIDC configuration + +_Appears in:_ + +- [api.v1alpha1.IncomingAuthConfig](#apiv1alpha1incomingauthconfig) +- [api.v1alpha1.MCPRemoteProxySpec](#apiv1alpha1mcpremoteproxyspec) +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------------------------------------------ | +| `type` _string_ | Type is the type of OIDC configuration | kubernetes | Enum: [kubernetes configMap inline]
| +| `resourceUrl` _string_ | ResourceURL is the explicit resource URL for OAuth discovery endpoint (RFC 9728)
If not specified, defaults to the in-cluster Kubernetes service URL | | Optional: \{\}
| +| `kubernetes` _[api.v1alpha1.KubernetesOIDCConfig](#apiv1alpha1kubernetesoidcconfig)_ | Kubernetes configures OIDC for Kubernetes service account token validation
Only used when Type is "kubernetes" | | Optional: \{\}
| +| `configMap` _[api.v1alpha1.ConfigMapOIDCRef](#apiv1alpha1configmapoidcref)_ | ConfigMap references a ConfigMap containing OIDC configuration
Only used when Type is "configmap" | | Optional: \{\}
| +| `inline` _[api.v1alpha1.InlineOIDCConfig](#apiv1alpha1inlineoidcconfig)_ | Inline contains direct OIDC configuration
Only used when Type is "inline" | | Optional: \{\}
| + +#### api.v1alpha1.OIDCUpstreamConfig + +OIDCUpstreamConfig contains configuration for OIDC providers. +OIDC providers support automatic endpoint discovery via the issuer URL. + +_Appears in:_ + +- [api.v1alpha1.UpstreamProviderConfig](#apiv1alpha1upstreamproviderconfig) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------------------------------------- | +| `issuerUrl` _string_ | IssuerURL is the OIDC issuer URL for automatic endpoint discovery.
Must be a valid HTTPS URL. | | Pattern: `^https://.*$`
Required: \{\}
| +| `clientId` _string_ | ClientID is the OAuth 2.0 client identifier registered with the upstream IDP. | | Required: \{\}
| +| `clientSecretRef` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref)_ | ClientSecretRef references a Kubernetes Secret containing the OAuth 2.0 client secret.
Optional for public clients using PKCE instead of client secret. | | Optional: \{\}
| +| `redirectUri` _string_ | RedirectURI is the callback URL where the upstream IDP will redirect after authentication.
When not specified, defaults to `\{resourceUrl\}/oauth/callback` where `resourceUrl` is the
URL associated with the resource (e.g., MCPServer or vMCP) using this config. | | Optional: \{\}
| +| `scopes` _string array_ | Scopes are the OAuth scopes to request from the upstream IDP.
If not specified, defaults to ["openid", "offline_access"]. | | Optional: \{\}
| +| `userInfoOverride` _[api.v1alpha1.UserInfoConfig](#apiv1alpha1userinfoconfig)_ | UserInfoOverride allows customizing UserInfo fetching behavior for OIDC providers.
By default, the UserInfo endpoint is discovered automatically via OIDC discovery.
Use this to override the endpoint URL, HTTP method, or field mappings for providers
that return non-standard claim names in their UserInfo response. | | Optional: \{\}
| + +#### api.v1alpha1.OpenTelemetryConfig + +OpenTelemetryConfig defines pure OpenTelemetry configuration + +_Appears in:_ + +- [api.v1alpha1.TelemetryConfig](#apiv1alpha1telemetryconfig) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `enabled` _boolean_ | Enabled controls whether OpenTelemetry is enabled | false | Optional: \{\}
| +| `endpoint` _string_ | Endpoint is the OTLP endpoint URL for tracing and metrics | | Optional: \{\}
| +| `serviceName` _string_ | ServiceName is the service name for telemetry
If not specified, defaults to the MCPServer name | | Optional: \{\}
| +| `headers` _string array_ | Headers contains authentication headers for the OTLP endpoint
Specified as key=value pairs | | Optional: \{\}
| +| `insecure` _boolean_ | Insecure indicates whether to use HTTP instead of HTTPS for the OTLP endpoint | false | Optional: \{\}
| +| `metrics` _[api.v1alpha1.OpenTelemetryMetricsConfig](#apiv1alpha1opentelemetrymetricsconfig)_ | Metrics defines OpenTelemetry metrics-specific configuration | | Optional: \{\}
| +| `tracing` _[api.v1alpha1.OpenTelemetryTracingConfig](#apiv1alpha1opentelemetrytracingconfig)_ | Tracing defines OpenTelemetry tracing configuration | | Optional: \{\}
| +| `useLegacyAttributes` _boolean_ | UseLegacyAttributes controls whether legacy attribute names are emitted alongside
the new MCP OTEL semantic convention names. Defaults to true for backward compatibility.
This will change to false in a future release and eventually be removed. | true | Optional: \{\}
| + +#### api.v1alpha1.OpenTelemetryMetricsConfig + +OpenTelemetryMetricsConfig defines OpenTelemetry metrics configuration + +_Appears in:_ + +- [api.v1alpha1.OpenTelemetryConfig](#apiv1alpha1opentelemetryconfig) + +| Field | Description | Default | Validation | +| ------------------- | ---------------------------------------------- | ------- | --------------------- | +| `enabled` _boolean_ | Enabled controls whether OTLP metrics are sent | false | Optional: \{\}
| + +#### api.v1alpha1.OpenTelemetryTracingConfig + +OpenTelemetryTracingConfig defines OpenTelemetry tracing configuration + +_Appears in:_ + +- [api.v1alpha1.OpenTelemetryConfig](#apiv1alpha1opentelemetryconfig) + +| Field | Description | Default | Validation | +| ----------------------- | ------------------------------------------------- | ------- | --------------------- | +| `enabled` _boolean_ | Enabled controls whether OTLP tracing is sent | false | Optional: \{\}
| +| `samplingRate` _string_ | SamplingRate is the trace sampling rate (0.0-1.0) | 0.05 | Optional: \{\}
| + +#### api.v1alpha1.OutboundNetworkPermissions + +OutboundNetworkPermissions defines the outbound network permissions + +_Appears in:_ + +- [api.v1alpha1.NetworkPermissions](#apiv1alpha1networkpermissions) + +| Field | Description | Default | Validation | +| ---------------------------- | -------------------------------------------------------------------------- | ------- | --------------------- | +| `insecureAllowAll` _boolean_ | InsecureAllowAll allows all outbound network connections (not recommended) | false | Optional: \{\}
| +| `allowHost` _string array_ | AllowHost is a list of hosts to allow connections to | | Optional: \{\}
| +| `allowPort` _integer array_ | AllowPort is a list of ports to allow connections to | | Optional: \{\}
| + +#### api.v1alpha1.OutgoingAuthConfig + +OutgoingAuthConfig configures authentication from Virtual MCP to backend MCPServers + +_Appears in:_ + +- [api.v1alpha1.VirtualMCPServerSpec](#apiv1alpha1virtualmcpserverspec) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ----------------------------------------------------- | +| `source` _string_ | Source defines how backend authentication configurations are determined
- discovered: Automatically discover from backend's MCPServer.spec.externalAuthConfigRef
- inline: Explicit per-backend configuration in VirtualMCPServer | discovered | Enum: [discovered inline]
Optional: \{\}
| +| `default` _[api.v1alpha1.BackendAuthConfig](#apiv1alpha1backendauthconfig)_ | Default defines default behavior for backends without explicit auth config | | Optional: \{\}
| +| `backends` _object (keys:string, values:[api.v1alpha1.BackendAuthConfig](#apiv1alpha1backendauthconfig))_ | Backends defines per-backend authentication overrides
Works in all modes (discovered, inline) | | Optional: \{\}
| + +#### api.v1alpha1.PVCSource + +PVCSource defines PersistentVolumeClaim source configuration + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryConfig](#apiv1alpha1mcpregistryconfig) + +| Field | Description | Default | Validation | +| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | ------------------------------------------------- | +| `claimName` _string_ | ClaimName is the name of the PersistentVolumeClaim | | MinLength: 1
Required: \{\}
| +| `path` _string_ | Path is the relative path to the registry file within the PVC.
The PVC is mounted at /config/registry/\{registryName\}/.
The full file path becomes: /config/registry/\{registryName\}/\{path\}
This design:
- Each registry gets its own mount point (consistent with ConfigMap sources)
- Multiple registries can share the same PVC by mounting it at different paths
- Users control PVC organization freely via the path field
Examples:
Registry "production" using PVC "shared-data" with path "prod/registry.json":
PVC contains /prod/registry.json → accessed at /config/registry/production/prod/registry.json
Registry "development" using SAME PVC "shared-data" with path "dev/registry.json":
PVC contains /dev/registry.json → accessed at /config/registry/development/dev/registry.json
(Same PVC, different mount path)
Registry "staging" using DIFFERENT PVC "other-pvc" with path "registry.json":
PVC contains /registry.json → accessed at /config/registry/staging/registry.json
(Different PVC, independent mount)
Registry "team-a" with path "v1/servers.json":
PVC contains /v1/servers.json → accessed at /config/registry/team-a/v1/servers.json
(Subdirectories allowed in path) | registry.json | Pattern: `^.*\.json$`
Optional: \{\}
| + +#### api.v1alpha1.PermissionProfileRef + +PermissionProfileRef defines a reference to a permission profile + +_Appears in:_ + +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) + +| Field | Description | Default | Validation | +| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -------------------------------- | +| `type` _string_ | Type is the type of permission profile reference | builtin | Enum: [builtin configmap]
| +| `name` _string_ | Name is the name of the permission profile
If Type is "builtin", Name must be one of: "none", "network"
If Type is "configmap", Name is the name of the ConfigMap | | Required: \{\}
| +| `key` _string_ | Key is the key in the ConfigMap that contains the permission profile
Only used when Type is "configmap" | | Optional: \{\}
| + +#### api.v1alpha1.PrometheusConfig + +PrometheusConfig defines Prometheus-specific configuration + +_Appears in:_ + +- [api.v1alpha1.TelemetryConfig](#apiv1alpha1telemetryconfig) + +| Field | Description | Default | Validation | +| ------------------- | --------------------------------------------------------------- | ------- | --------------------- | +| `enabled` _boolean_ | Enabled controls whether Prometheus metrics endpoint is exposed | false | Optional: \{\}
| + +#### api.v1alpha1.ProxyDeploymentOverrides + +ProxyDeploymentOverrides defines overrides specific to the proxy deployment + +_Appears in:_ + +- [api.v1alpha1.ResourceOverrides](#apiv1alpha1resourceoverrides) + +| Field | Description | Default | Validation | +| ---------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `annotations` _object (keys:string, values:string)_ | Annotations to add or override on the resource | | Optional: \{\}
| +| `labels` _object (keys:string, values:string)_ | Labels to add or override on the resource | | Optional: \{\}
| +| `podTemplateMetadataOverrides` _[api.v1alpha1.ResourceMetadataOverrides](#apiv1alpha1resourcemetadataoverrides)_ | | | | +| `env` _[api.v1alpha1.EnvVar](#apiv1alpha1envvar) array_ | Env are environment variables to set in the proxy container (thv run process)
These affect the toolhive proxy itself, not the MCP server it manages
Use TOOLHIVE_DEBUG=true to enable debug logging in the proxy | | Optional: \{\}
| +| `imagePullSecrets` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#localobjectreference-v1-core) array_ | ImagePullSecrets allows specifying image pull secrets for the proxy runner
These are applied to both the Deployment and the ServiceAccount | | Optional: \{\}
| + +#### api.v1alpha1.RedisACLUserConfig + +RedisACLUserConfig configures Redis ACL user authentication. + +_Appears in:_ + +- [api.v1alpha1.RedisStorageConfig](#apiv1alpha1redisstorageconfig) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ------- | --------------------- | +| `usernameSecretRef` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref)_ | UsernameSecretRef references a Secret containing the Redis ACL username. | | Required: \{\}
| +| `passwordSecretRef` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref)_ | PasswordSecretRef references a Secret containing the Redis ACL password. | | Required: \{\}
| + +#### api.v1alpha1.RedisSentinelConfig + +RedisSentinelConfig configures Redis Sentinel connection. + +_Appears in:_ + +- [api.v1alpha1.RedisStorageConfig](#apiv1alpha1redisstorageconfig) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | ------- | --------------------- | +| `masterName` _string_ | MasterName is the name of the Redis master monitored by Sentinel. | | Required: \{\}
| +| `sentinelAddrs` _string array_ | SentinelAddrs is a list of Sentinel host:port addresses.
Mutually exclusive with SentinelService. | | Optional: \{\}
| +| `sentinelService` _[api.v1alpha1.SentinelServiceRef](#apiv1alpha1sentinelserviceref)_ | SentinelService enables automatic discovery from a Kubernetes Service.
Mutually exclusive with SentinelAddrs. | | Optional: \{\}
| +| `db` _integer_ | DB is the Redis database number. | 0 | Optional: \{\}
| + +#### api.v1alpha1.RedisStorageConfig + +RedisStorageConfig configures Redis connection for auth server storage. +Redis is deployed in Sentinel mode with ACL user authentication (the only supported configuration). + +_Appears in:_ + +- [api.v1alpha1.AuthServerStorageConfig](#apiv1alpha1authserverstorageconfig) + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------- | +| `sentinelConfig` _[api.v1alpha1.RedisSentinelConfig](#apiv1alpha1redissentinelconfig)_ | SentinelConfig holds Redis Sentinel configuration. | | Required: \{\}
| +| `aclUserConfig` _[api.v1alpha1.RedisACLUserConfig](#apiv1alpha1redisacluserconfig)_ | ACLUserConfig configures Redis ACL user authentication. | | Required: \{\}
| +| `dialTimeout` _string_ | DialTimeout is the timeout for establishing connections.
Format: Go duration string (e.g., "5s", "1m"). | 5s | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Optional: \{\}
| +| `readTimeout` _string_ | ReadTimeout is the timeout for socket reads.
Format: Go duration string (e.g., "3s", "1m"). | 3s | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Optional: \{\}
| +| `writeTimeout` _string_ | WriteTimeout is the timeout for socket writes.
Format: Go duration string (e.g., "3s", "1m"). | 3s | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Optional: \{\}
| +| `tls` _[api.v1alpha1.RedisTLSConfig](#apiv1alpha1redistlsconfig)_ | TLS configures TLS for connections to the Redis/Valkey master.
Presence of this field enables TLS. Omit to use plaintext. | | Optional: \{\}
| +| `sentinelTls` _[api.v1alpha1.RedisTLSConfig](#apiv1alpha1redistlsconfig)_ | SentinelTLS configures TLS for connections to Sentinel instances.
Presence of this field enables TLS. Omit to use plaintext.
When omitted, sentinel connections use plaintext (no fallback to TLS config). | | Optional: \{\}
| + +#### api.v1alpha1.RedisTLSConfig + +RedisTLSConfig configures TLS for Redis connections. +Presence of this struct on a connection type enables TLS for that connection. + +_Appears in:_ + +- [api.v1alpha1.RedisStorageConfig](#apiv1alpha1redisstorageconfig) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `insecureSkipVerify` _boolean_ | InsecureSkipVerify skips TLS certificate verification.
Use when connecting to services with self-signed certificates. | | Optional: \{\}
| +| `caCertSecretRef` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref)_ | CACertSecretRef references a Secret containing a PEM-encoded CA certificate
for verifying the server. When not specified, system root CAs are used. | | Optional: \{\}
| + +#### api.v1alpha1.RegistryFilter + +RegistryFilter defines include/exclude patterns for registry content + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryConfig](#apiv1alpha1mcpregistryconfig) + +| Field | Description | Default | Validation | +| ----------------------------------------------------------- | ---------------------------------------- | ------- | --------------------- | +| `names` _[api.v1alpha1.NameFilter](#apiv1alpha1namefilter)_ | NameFilters defines name-based filtering | | Optional: \{\}
| +| `tags` _[api.v1alpha1.TagFilter](#apiv1alpha1tagfilter)_ | Tags defines tag-based filtering | | Optional: \{\}
| + +#### api.v1alpha1.ResourceList + +ResourceList is a set of (resource name, quantity) pairs + +_Appears in:_ + +- [api.v1alpha1.ResourceRequirements](#apiv1alpha1resourcerequirements) + +| Field | Description | Default | Validation | +| ----------------- | ------------------------------------------------------------------- | ------- | --------------------- | +| `cpu` _string_ | CPU is the CPU limit in cores (e.g., "500m" for 0.5 cores) | | Optional: \{\}
| +| `memory` _string_ | Memory is the memory limit in bytes (e.g., "64Mi" for 64 megabytes) | | Optional: \{\}
| + +#### api.v1alpha1.ResourceMetadataOverrides + +ResourceMetadataOverrides defines metadata overrides for a resource + +_Appears in:_ + +- [api.v1alpha1.EmbeddingResourceOverrides](#apiv1alpha1embeddingresourceoverrides) +- [api.v1alpha1.EmbeddingStatefulSetOverrides](#apiv1alpha1embeddingstatefulsetoverrides) +- [api.v1alpha1.ProxyDeploymentOverrides](#apiv1alpha1proxydeploymentoverrides) +- [api.v1alpha1.ResourceOverrides](#apiv1alpha1resourceoverrides) + +| Field | Description | Default | Validation | +| --------------------------------------------------- | ---------------------------------------------- | ------- | --------------------- | +| `annotations` _object (keys:string, values:string)_ | Annotations to add or override on the resource | | Optional: \{\}
| +| `labels` _object (keys:string, values:string)_ | Labels to add or override on the resource | | Optional: \{\}
| + +#### api.v1alpha1.ResourceOverrides + +ResourceOverrides defines overrides for annotations and labels on created resources + +_Appears in:_ + +- [api.v1alpha1.MCPRemoteProxySpec](#apiv1alpha1mcpremoteproxyspec) +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `proxyDeployment` _[api.v1alpha1.ProxyDeploymentOverrides](#apiv1alpha1proxydeploymentoverrides)_ | ProxyDeployment defines overrides for the Proxy Deployment resource (toolhive proxy) | | Optional: \{\}
| +| `proxyService` _[api.v1alpha1.ResourceMetadataOverrides](#apiv1alpha1resourcemetadataoverrides)_ | ProxyService defines overrides for the Proxy Service resource (points to the proxy deployment) | | Optional: \{\}
| + +#### api.v1alpha1.ResourceRequirements + +ResourceRequirements describes the compute resource requirements + +_Appears in:_ + +- [api.v1alpha1.EmbeddingServerSpec](#apiv1alpha1embeddingserverspec) +- [api.v1alpha1.MCPRemoteProxySpec](#apiv1alpha1mcpremoteproxyspec) +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------ | ------------------------------------------------------------------- | ------- | --------------------- | +| `limits` _[api.v1alpha1.ResourceList](#apiv1alpha1resourcelist)_ | Limits describes the maximum amount of compute resources allowed | | Optional: \{\}
| +| `requests` _[api.v1alpha1.ResourceList](#apiv1alpha1resourcelist)_ | Requests describes the minimum amount of compute resources required | | Optional: \{\}
| + +#### api.v1alpha1.RoleMapping + +RoleMapping defines a rule for mapping JWT claims to IAM roles. +Mappings are evaluated in priority order (lower number = higher priority), and the first +matching rule determines which IAM role to assume. +Exactly one of Claim or Matcher must be specified. + +_Appears in:_ + +- [api.v1alpha1.AWSStsConfig](#apiv1alpha1awsstsconfig) + +| Field | Description | Default | Validation | +| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------- | +| `claim` _string_ | Claim is a simple claim value to match against
The claim type is specified by AWSStsConfig.RoleClaim
For example, if RoleClaim is "groups", this would be a group name
Internally compiled to a CEL expression: "" in claims[""]
Mutually exclusive with Matcher | | MinLength: 1
Optional: \{\}
| +| `matcher` _string_ | Matcher is a CEL expression for complex matching against JWT claims
The expression has access to a "claims" variable containing all JWT claims as map[string]any
Examples:
- "admins" in claims["groups"]
- claims["sub"] == "user123" && !("act" in claims)
Mutually exclusive with Claim | | MinLength: 1
Optional: \{\}
| +| `roleArn` _string_ | RoleArn is the IAM role ARN to assume when this mapping matches | | Pattern: `^arn:(aws\|aws-cn\|aws-us-gov):iam::\d\{12\}:role/[\w+=,.@\-_/]+$`
Required: \{\}
| +| `priority` _integer_ | Priority determines evaluation order (lower values = higher priority)
Allows fine-grained control over role selection precedence
When omitted, this mapping has the lowest possible priority and
configuration order acts as tie-breaker via stable sort | | Minimum: 0
Optional: \{\}
| + +#### api.v1alpha1.SecretKeyRef + +SecretKeyRef is a reference to a key within a Secret + +_Appears in:_ + +- [api.v1alpha1.BearerTokenConfig](#apiv1alpha1bearertokenconfig) +- [api.v1alpha1.EmbeddedAuthServerConfig](#apiv1alpha1embeddedauthserverconfig) +- [api.v1alpha1.EmbeddingServerSpec](#apiv1alpha1embeddingserverspec) +- [api.v1alpha1.HeaderFromSecret](#apiv1alpha1headerfromsecret) +- [api.v1alpha1.HeaderInjectionConfig](#apiv1alpha1headerinjectionconfig) +- [api.v1alpha1.InlineOIDCConfig](#apiv1alpha1inlineoidcconfig) +- [api.v1alpha1.OAuth2UpstreamConfig](#apiv1alpha1oauth2upstreamconfig) +- [api.v1alpha1.OIDCUpstreamConfig](#apiv1alpha1oidcupstreamconfig) +- [api.v1alpha1.RedisACLUserConfig](#apiv1alpha1redisacluserconfig) +- [api.v1alpha1.RedisTLSConfig](#apiv1alpha1redistlsconfig) +- [api.v1alpha1.TokenExchangeConfig](#apiv1alpha1tokenexchangeconfig) + +| Field | Description | Default | Validation | +| --------------- | -------------------------------- | ------- | --------------------- | +| `name` _string_ | Name is the name of the secret | | Required: \{\}
| +| `key` _string_ | Key is the key within the secret | | Required: \{\}
| + +#### api.v1alpha1.SecretRef + +SecretRef is a reference to a secret + +_Appears in:_ + +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) + +| Field | Description | Default | Validation | +| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `name` _string_ | Name is the name of the secret | | Required: \{\}
| +| `key` _string_ | Key is the key in the secret itself | | Required: \{\}
| +| `targetEnvName` _string_ | TargetEnvName is the environment variable to be used when setting up the secret in the MCP server
If left unspecified, it defaults to the key | | Optional: \{\}
| + +#### api.v1alpha1.SentinelServiceRef + +_Underlying type:_ _[api.v1alpha1.struct{Name string "json:\"name\""; Namespace string "json:\"namespace,omitempty\""; Port int32 "json:\"port,omitempty\""}](#apiv1alpha1struct{name string "json:\"name\""; namespace string "json:\"namespace,omitempty\""; port int32 "json:\"port,omitempty\""})_ + +SentinelServiceRef references a Kubernetes Service for Sentinel discovery. + +_Appears in:_ + +- [api.v1alpha1.RedisSentinelConfig](#apiv1alpha1redissentinelconfig) + +#### api.v1alpha1.StorageReference + +StorageReference defines a reference to internal storage + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryStatus](#apiv1alpha1mcpregistrystatus) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ | ------- | ------------------------ | +| `type` _string_ | Type is the storage type (configmap) | | Enum: [configmap]
| +| `configMapRef` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#localobjectreference-v1-core)_ | ConfigMapRef is a reference to a ConfigMap storage
Only used when Type is "configmap" | | Optional: \{\}
| + +#### api.v1alpha1.SyncPhase + +_Underlying type:_ _string_ + +SyncPhase represents the data synchronization state + +_Validation:_ + +- Enum: [Syncing Complete Failed] + +_Appears in:_ + +- [api.v1alpha1.SyncStatus](#apiv1alpha1syncstatus) + +| Field | Description | +| ---------- | ---------------------------------------------------------- | +| `Syncing` | SyncPhaseSyncing means sync is currently in progress
| +| `Complete` | SyncPhaseComplete means sync completed successfully
| +| `Failed` | SyncPhaseFailed means sync failed
| + +#### api.v1alpha1.SyncPolicy + +SyncPolicy defines automatic synchronization behavior. +When specified, enables automatic synchronization at the given interval. +Manual synchronization via annotation-based triggers is always available +regardless of this policy setting. + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryConfig](#apiv1alpha1mcpregistryconfig) + +| Field | Description | Default | Validation | +| ------------------- | ------------------------------------------------------------------------------------------------------------------ | ------- | -------------------------------------------------------------------------------------- | +| `interval` _string_ | Interval is the sync interval for automatic synchronization (Go duration format)
Examples: "1h", "30m", "24h" | | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Required: \{\}
| + +#### api.v1alpha1.SyncStatus + +SyncStatus provides detailed information about data synchronization + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryStatus](#apiv1alpha1mcpregistrystatus) + +| Field | Description | Default | Validation | +| ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------- | -------------------------------------- | +| `phase` _[api.v1alpha1.SyncPhase](#apiv1alpha1syncphase)_ | Phase represents the current synchronization phase | | Enum: [Syncing Complete Failed]
| +| `message` _string_ | Message provides additional information about the sync status | | Optional: \{\}
| +| `lastAttempt` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#time-v1-meta)_ | LastAttempt is the timestamp of the last sync attempt | | Optional: \{\}
| +| `attemptCount` _integer_ | AttemptCount is the number of sync attempts since last success | | Minimum: 0
Optional: \{\}
| +| `lastSyncTime` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#time-v1-meta)_ | LastSyncTime is the timestamp of the last successful sync | | Optional: \{\}
| +| `lastSyncHash` _string_ | LastSyncHash is the hash of the last successfully synced data
Used to detect changes in source data | | Optional: \{\}
| +| `serverCount` _integer_ | ServerCount is the total number of servers in the registry | | Minimum: 0
Optional: \{\}
| + +#### api.v1alpha1.TagFilter + +TagFilter defines tag-based filtering + +_Appears in:_ + +- [api.v1alpha1.RegistryFilter](#apiv1alpha1registryfilter) + +| Field | Description | Default | Validation | +| ------------------------ | ------------------------------------ | ------- | --------------------- | +| `include` _string array_ | Include is a list of tags to include | | Optional: \{\}
| +| `exclude` _string array_ | Exclude is a list of tags to exclude | | Optional: \{\}
| + +#### api.v1alpha1.TelemetryConfig + +TelemetryConfig defines observability configuration for the MCP server + +_Appears in:_ + +- [api.v1alpha1.MCPRemoteProxySpec](#apiv1alpha1mcpremoteproxyspec) +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------- | ---------------------------------------------------- | ------- | --------------------- | +| `openTelemetry` _[api.v1alpha1.OpenTelemetryConfig](#apiv1alpha1opentelemetryconfig)_ | OpenTelemetry defines OpenTelemetry configuration | | Optional: \{\}
| +| `prometheus` _[api.v1alpha1.PrometheusConfig](#apiv1alpha1prometheusconfig)_ | Prometheus defines Prometheus-specific configuration | | Optional: \{\}
| + +#### api.v1alpha1.TokenExchangeConfig + +TokenExchangeConfig holds configuration for RFC-8693 OAuth 2.0 Token Exchange. +This configuration is used to exchange incoming authentication tokens for tokens +that can be used with external services. +The structure matches the tokenexchange.Config from pkg/auth/tokenexchange/middleware.go + +_Appears in:_ + +- [api.v1alpha1.MCPExternalAuthConfigSpec](#apiv1alpha1mcpexternalauthconfigspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------- | +| `tokenUrl` _string_ | TokenURL is the OAuth 2.0 token endpoint URL for token exchange | | Required: \{\}
| +| `clientId` _string_ | ClientID is the OAuth 2.0 client identifier
Optional for some token exchange flows (e.g., Google Cloud Workforce Identity) | | Optional: \{\}
| +| `clientSecretRef` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref)_ | ClientSecretRef is a reference to a secret containing the OAuth 2.0 client secret
Optional for some token exchange flows (e.g., Google Cloud Workforce Identity) | | Optional: \{\}
| +| `audience` _string_ | Audience is the target audience for the exchanged token | | Required: \{\}
| +| `scopes` _string array_ | Scopes is a list of OAuth 2.0 scopes to request for the exchanged token | | Optional: \{\}
| +| `subjectTokenType` _string_ | SubjectTokenType is the type of the incoming subject token.
Accepts short forms: "access_token" (default), "id_token", "jwt"
Or full URNs: "urn:ietf:params:oauth:token-type:access_token",
"urn:ietf:params:oauth:token-type:id_token",
"urn:ietf:params:oauth:token-type:jwt"
For Google Workload Identity Federation with OIDC providers (like Okta), use "id_token" | | Pattern: `^(access_token\|id_token\|jwt\|urn:ietf:params:oauth:token-type:(access_token\|id_token\|jwt))?$`
Optional: \{\}
| +| `externalTokenHeaderName` _string_ | ExternalTokenHeaderName is the name of the custom header to use for the exchanged token.
If set, the exchanged token will be added to this custom header (e.g., "X-Upstream-Token").
If empty or not set, the exchanged token will replace the Authorization header (default behavior). | | Optional: \{\}
| + +#### api.v1alpha1.TokenLifespanConfig + +TokenLifespanConfig holds configuration for token lifetimes. + +_Appears in:_ + +- [api.v1alpha1.EmbeddedAuthServerConfig](#apiv1alpha1embeddedauthserverconfig) + +| Field | Description | Default | Validation | +| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------- | +| `accessTokenLifespan` _string_ | AccessTokenLifespan is the duration that access tokens are valid.
Format: Go duration string (e.g., "1h", "30m", "24h").
If empty, defaults to 1 hour. | | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Optional: \{\}
| +| `refreshTokenLifespan` _string_ | RefreshTokenLifespan is the duration that refresh tokens are valid.
Format: Go duration string (e.g., "168h", "7d" as "168h").
If empty, defaults to 7 days (168h). | | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Optional: \{\}
| +| `authCodeLifespan` _string_ | AuthCodeLifespan is the duration that authorization codes are valid.
Format: Go duration string (e.g., "10m", "5m").
If empty, defaults to 10 minutes. | | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Optional: \{\}
| + +#### api.v1alpha1.TokenResponseMapping + +TokenResponseMapping maps non-standard token response fields to standard OAuth 2.0 fields +using dot-notation JSON paths. This supports upstream providers like GovSlack that nest +the access token under paths like "authed_user.access_token". + +_Appears in:_ + +- [api.v1alpha1.OAuth2UpstreamConfig](#apiv1alpha1oauth2upstreamconfig) + +| Field | Description | Default | Validation | +| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------- | +| `accessTokenPath` _string_ | AccessTokenPath is the dot-notation path to the access token in the response.
Example: "authed_user.access_token" | | MinLength: 1
Required: \{\}
| +| `scopePath` _string_ | ScopePath is the dot-notation path to the scope string in the response.
If not specified, defaults to "scope". | | Optional: \{\}
| +| `refreshTokenPath` _string_ | RefreshTokenPath is the dot-notation path to the refresh token in the response.
If not specified, defaults to "refresh_token". | | Optional: \{\}
| +| `expiresInPath` _string_ | ExpiresInPath is the dot-notation path to the expires_in value (in seconds).
If not specified, defaults to "expires_in". | | Optional: \{\}
| + +#### api.v1alpha1.ToolAnnotationsOverride + +ToolAnnotationsOverride defines overrides for tool annotation fields. +All fields use pointers so nil means "don't override" while zero values +(empty string, false) mean "explicitly set to this value." + +_Appears in:_ + +- [api.v1alpha1.ToolOverride](#apiv1alpha1tooloverride) + +| Field | Description | Default | Validation | +| --------------------------- | ---------------------------------------------------------- | ------- | --------------------- | +| `title` _string_ | Title overrides the human-readable title annotation. | | Optional: \{\}
| +| `readOnlyHint` _boolean_ | ReadOnlyHint overrides the read-only hint annotation. | | Optional: \{\}
| +| `destructiveHint` _boolean_ | DestructiveHint overrides the destructive hint annotation. | | Optional: \{\}
| +| `idempotentHint` _boolean_ | IdempotentHint overrides the idempotent hint annotation. | | Optional: \{\}
| +| `openWorldHint` _boolean_ | OpenWorldHint overrides the open-world hint annotation. | | Optional: \{\}
| + +#### api.v1alpha1.ToolConfigRef + +ToolConfigRef defines a reference to a MCPToolConfig resource. +The referenced MCPToolConfig must be in the same namespace as the MCPServer. + +_Appears in:_ + +- [api.v1alpha1.MCPRemoteProxySpec](#apiv1alpha1mcpremoteproxyspec) +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) + +| Field | Description | Default | Validation | +| --------------- | -------------------------------------------------------------------- | ------- | --------------------- | +| `name` _string_ | Name is the name of the MCPToolConfig resource in the same namespace | | Required: \{\}
| + +#### api.v1alpha1.ToolOverride + +ToolOverride represents a tool override configuration. +Both Name and Description can be overridden independently, but +they can't be both empty. + +_Appears in:_ + +- [api.v1alpha1.MCPToolConfigSpec](#apiv1alpha1mcptoolconfigspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `name` _string_ | Name is the redefined name of the tool | | Optional: \{\}
| +| `description` _string_ | Description is the redefined description of the tool | | Optional: \{\}
| +| `annotations` _[api.v1alpha1.ToolAnnotationsOverride](#apiv1alpha1toolannotationsoverride)_ | Annotations overrides specific tool annotation fields.
Only specified fields are overridden; others pass through from the backend. | | Optional: \{\}
| + +#### api.v1alpha1.UpstreamProviderConfig + +UpstreamProviderConfig defines configuration for an upstream Identity Provider. + +_Appears in:_ + +- [api.v1alpha1.EmbeddedAuthServerConfig](#apiv1alpha1embeddedauthserverconfig) + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------- | ------- | ----------------------------------------------- | +| `name` _string_ | Name uniquely identifies this upstream provider.
Used for routing decisions and session binding in multi-upstream scenarios. | | MinLength: 1
Required: \{\}
| +| `type` _[api.v1alpha1.UpstreamProviderType](#apiv1alpha1upstreamprovidertype)_ | Type specifies the provider type: "oidc" or "oauth2" | | Enum: [oidc oauth2]
Required: \{\}
| +| `oidcConfig` _[api.v1alpha1.OIDCUpstreamConfig](#apiv1alpha1oidcupstreamconfig)_ | OIDCConfig contains OIDC-specific configuration.
Required when Type is "oidc", must be nil when Type is "oauth2". | | Optional: \{\}
| +| `oauth2Config` _[api.v1alpha1.OAuth2UpstreamConfig](#apiv1alpha1oauth2upstreamconfig)_ | OAuth2Config contains OAuth 2.0-specific configuration.
Required when Type is "oauth2", must be nil when Type is "oidc". | | Optional: \{\}
| + +#### api.v1alpha1.UpstreamProviderType + +_Underlying type:_ _string_ + +UpstreamProviderType identifies the type of upstream Identity Provider. + +_Appears in:_ + +- [api.v1alpha1.UpstreamProviderConfig](#apiv1alpha1upstreamproviderconfig) + +| Field | Description | +| -------- | ---------------------------------------------------------------------------------------- | +| `oidc` | UpstreamProviderTypeOIDC is for OIDC providers with discovery support
| +| `oauth2` | UpstreamProviderTypeOAuth2 is for pure OAuth 2.0 providers with explicit endpoints
| + +#### api.v1alpha1.UserInfoConfig + +UserInfoConfig contains configuration for fetching user information from an upstream provider. +This supports both standard OIDC UserInfo endpoints and custom provider-specific endpoints +like GitHub's /user API. + +_Appears in:_ + +- [api.v1alpha1.OAuth2UpstreamConfig](#apiv1alpha1oauth2upstreamconfig) +- [api.v1alpha1.OIDCUpstreamConfig](#apiv1alpha1oidcupstreamconfig) + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------------------- | +| `endpointUrl` _string_ | EndpointURL is the URL of the userinfo endpoint. | | Pattern: `^https?://.*$`
Required: \{\}
| +| `httpMethod` _string_ | HTTPMethod is the HTTP method to use for the userinfo request.
If not specified, defaults to GET. | | Enum: [GET POST]
Optional: \{\}
| +| `additionalHeaders` _object (keys:string, values:string)_ | AdditionalHeaders contains extra headers to include in the userinfo request.
Useful for providers that require specific headers (e.g., GitHub's Accept header). | | Optional: \{\}
| +| `fieldMapping` _[api.v1alpha1.UserInfoFieldMapping](#apiv1alpha1userinfofieldmapping)_ | FieldMapping contains custom field mapping configuration for non-standard providers.
If nil, standard OIDC field names are used ("sub", "name", "email"). | | Optional: \{\}
| + +#### api.v1alpha1.UserInfoFieldMapping + +_Underlying type:_ _[api.v1alpha1.struct{SubjectFields []string "json:\"subjectFields,omitempty\""; NameFields []string "json:\"nameFields,omitempty\""; EmailFields []string "json:\"emailFields,omitempty\""}](#apiv1alpha1struct{subjectfields []string "json:\"subjectfields,omitempty\""; namefields []string "json:\"namefields,omitempty\""; emailfields []string "json:\"emailfields,omitempty\""})_ + +UserInfoFieldMapping maps provider-specific field names to standard UserInfo fields. +This allows adapting non-standard provider responses to the canonical UserInfo structure. +Each field supports an ordered list of claim names to try. The first non-empty value +found will be used. + +Example for GitHub: + + fieldMapping: + subjectFields: ["id", "login"] + nameFields: ["name", "login"] + emailFields: ["email"] + +_Appears in:_ + +- [api.v1alpha1.UserInfoConfig](#apiv1alpha1userinfoconfig) + +#### api.v1alpha1.ValidationStatus + +_Underlying type:_ _string_ + +ValidationStatus represents the validation state of a workflow + +_Validation:_ + +- Enum: [Valid Invalid Unknown] + +_Appears in:_ + +- [api.v1alpha1.VirtualMCPCompositeToolDefinitionStatus](#apiv1alpha1virtualmcpcompositetooldefinitionstatus) + +| Field | Description | +| --------- | ---------------------------------------------------------------------------- | +| `Valid` | ValidationStatusValid indicates the workflow is valid
| +| `Invalid` | ValidationStatusInvalid indicates the workflow has validation errors
| +| `Unknown` | ValidationStatusUnknown indicates validation hasn't been performed yet
| + +#### api.v1alpha1.VirtualMCPCompositeToolDefinition + +VirtualMCPCompositeToolDefinition is the Schema for the virtualmcpcompositetooldefinitions API +VirtualMCPCompositeToolDefinition defines reusable composite workflows that can be referenced +by multiple VirtualMCPServer instances + +_Appears in:_ + +- [api.v1alpha1.VirtualMCPCompositeToolDefinitionList](#apiv1alpha1virtualmcpcompositetooldefinitionlist) + +| Field | Description | Default | Validation | +| ---------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `VirtualMCPCompositeToolDefinition` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `spec` _[api.v1alpha1.VirtualMCPCompositeToolDefinitionSpec](#apiv1alpha1virtualmcpcompositetooldefinitionspec)_ | | | | +| `status` _[api.v1alpha1.VirtualMCPCompositeToolDefinitionStatus](#apiv1alpha1virtualmcpcompositetooldefinitionstatus)_ | | | | + +#### api.v1alpha1.VirtualMCPCompositeToolDefinitionList + +VirtualMCPCompositeToolDefinitionList contains a list of VirtualMCPCompositeToolDefinition + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `VirtualMCPCompositeToolDefinitionList` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `items` _[api.v1alpha1.VirtualMCPCompositeToolDefinition](#apiv1alpha1virtualmcpcompositetooldefinition) array_ | | | | + +#### api.v1alpha1.VirtualMCPCompositeToolDefinitionSpec + +VirtualMCPCompositeToolDefinitionSpec defines the desired state of VirtualMCPCompositeToolDefinition. +This embeds the CompositeToolConfig from pkg/vmcp/config to share the configuration model +between CLI and operator usage. + +_Appears in:_ + +- [api.v1alpha1.VirtualMCPCompositeToolDefinition](#apiv1alpha1virtualmcpcompositetooldefinition) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------ | +| `name` _string_ | Name is the workflow name (unique identifier). | | | +| `description` _string_ | Description describes what the workflow does. | | | +| `parameters` _[pkg.json.Map](#pkgjsonmap)_ | Parameters defines input parameter schema in JSON Schema format.
Should be a JSON Schema object with "type": "object" and "properties".
Example:
\{
"type": "object",
"properties": \{
"param1": \{"type": "string", "default": "value"\},
"param2": \{"type": "integer"\}
\},
"required": ["param2"]
\}
We use json.Map rather than a typed struct because JSON Schema is highly
flexible with many optional fields (default, enum, minimum, maximum, pattern,
items, additionalProperties, oneOf, anyOf, allOf, etc.). Using json.Map
allows full JSON Schema compatibility without needing to define every possible
field, and matches how the MCP SDK handles inputSchema. | | Optional: \{\}
| +| `timeout` _[vmcp.config.Duration](#vmcpconfigduration)_ | Timeout is the maximum workflow execution time. | | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Type: string
| +| `steps` _[vmcp.config.WorkflowStepConfig](#vmcpconfigworkflowstepconfig) array_ | Steps are the workflow steps to execute. | | | +| `output` _[vmcp.config.OutputConfig](#vmcpconfigoutputconfig)_ | Output defines the structured output schema for this workflow.
If not specified, the workflow returns the last step's output (backward compatible). | | Optional: \{\}
| + +#### api.v1alpha1.VirtualMCPCompositeToolDefinitionStatus + +VirtualMCPCompositeToolDefinitionStatus defines the observed state of VirtualMCPCompositeToolDefinition + +_Appears in:_ + +- [api.v1alpha1.VirtualMCPCompositeToolDefinition](#apiv1alpha1virtualmcpcompositetooldefinition) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------------------------------------------- | +| `validationStatus` _[api.v1alpha1.ValidationStatus](#apiv1alpha1validationstatus)_ | ValidationStatus indicates the validation state of the workflow
- Valid: Workflow structure is valid
- Invalid: Workflow has validation errors | | Enum: [Valid Invalid Unknown]
Optional: \{\}
| +| `validationErrors` _string array_ | ValidationErrors contains validation error messages if ValidationStatus is Invalid | | Optional: \{\}
| +| `referencingVirtualServers` _string array_ | ReferencingVirtualServers lists VirtualMCPServer resources that reference this workflow
This helps track which servers need to be reconciled when this workflow changes | | Optional: \{\}
| +| `observedGeneration` _integer_ | ObservedGeneration is the most recent generation observed for this VirtualMCPCompositeToolDefinition
It corresponds to the resource's generation, which is updated on mutation by the API Server | | Optional: \{\}
| +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#condition-v1-meta) array_ | Conditions represent the latest available observations of the workflow's state | | Optional: \{\}
| + +#### api.v1alpha1.VirtualMCPServer + +VirtualMCPServer is the Schema for the virtualmcpservers API +VirtualMCPServer aggregates multiple backend MCPServers into a unified endpoint + +_Appears in:_ + +- [api.v1alpha1.VirtualMCPServerList](#apiv1alpha1virtualmcpserverlist) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `VirtualMCPServer` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `spec` _[api.v1alpha1.VirtualMCPServerSpec](#apiv1alpha1virtualmcpserverspec)_ | | | | +| `status` _[api.v1alpha1.VirtualMCPServerStatus](#apiv1alpha1virtualmcpserverstatus)_ | | | | + +#### api.v1alpha1.VirtualMCPServerList + +VirtualMCPServerList contains a list of VirtualMCPServer + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `VirtualMCPServerList` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `items` _[api.v1alpha1.VirtualMCPServer](#apiv1alpha1virtualmcpserver) array_ | | | | + +#### api.v1alpha1.VirtualMCPServerPhase + +_Underlying type:_ _string_ + +VirtualMCPServerPhase represents the lifecycle phase of a VirtualMCPServer + +_Validation:_ + +- Enum: [Pending Ready Degraded Failed] + +_Appears in:_ + +- [api.v1alpha1.VirtualMCPServerStatus](#apiv1alpha1virtualmcpserverstatus) + +| Field | Description | +| ---------- | --------------------------------------------------------------------------------------------------------------- | +| `Pending` | VirtualMCPServerPhasePending indicates the VirtualMCPServer is being initialized
| +| `Ready` | VirtualMCPServerPhaseReady indicates the VirtualMCPServer is ready and serving requests
| +| `Degraded` | VirtualMCPServerPhaseDegraded indicates the VirtualMCPServer is running but some backends are unavailable
| +| `Failed` | VirtualMCPServerPhaseFailed indicates the VirtualMCPServer has failed
| + +#### api.v1alpha1.VirtualMCPServerSpec + +VirtualMCPServerSpec defines the desired state of VirtualMCPServer + +_Appears in:_ + +- [api.v1alpha1.VirtualMCPServer](#apiv1alpha1virtualmcpserver) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------- | +| `incomingAuth` _[api.v1alpha1.IncomingAuthConfig](#apiv1alpha1incomingauthconfig)_ | IncomingAuth configures authentication for clients connecting to the Virtual MCP server.
Must be explicitly set - use "anonymous" type when no authentication is required.
This field takes precedence over config.IncomingAuth and should be preferred because it
supports Kubernetes-native secret references (SecretKeyRef, ConfigMapRef) for secure
dynamic discovery of credentials, rather than requiring secrets to be embedded in config. | | Required: \{\}
| +| `outgoingAuth` _[api.v1alpha1.OutgoingAuthConfig](#apiv1alpha1outgoingauthconfig)_ | OutgoingAuth configures authentication from Virtual MCP to backend MCPServers.
This field takes precedence over config.OutgoingAuth and should be preferred because it
supports Kubernetes-native secret references (SecretKeyRef, ConfigMapRef) for secure
dynamic discovery of credentials, rather than requiring secrets to be embedded in config. | | Optional: \{\}
| +| `serviceType` _string_ | ServiceType specifies the Kubernetes service type for the Virtual MCP server | ClusterIP | Enum: [ClusterIP NodePort LoadBalancer]
Optional: \{\}
| +| `sessionAffinity` _string_ | SessionAffinity controls whether the Service routes repeated client connections to the same pod.
MCP protocols (SSE, streamable-http) are stateful, so ClientIP is the default.
Set to "None" for stateless servers or when using an external load balancer with its own affinity. | ClientIP | Enum: [ClientIP None]
Optional: \{\}
| +| `serviceAccount` _string_ | ServiceAccount is the name of an already existing service account to use by the Virtual MCP server.
If not specified, a ServiceAccount will be created automatically and used by the Virtual MCP server. | | Optional: \{\}
| +| `podTemplateSpec` _[RawExtension](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#rawextension-runtime-pkg)_ | PodTemplateSpec defines the pod template to use for the Virtual MCP server
This allows for customizing the pod configuration beyond what is provided by the other fields.
Note that to modify the specific container the Virtual MCP server runs in, you must specify
the 'vmcp' container name in the PodTemplateSpec.
This field accepts a PodTemplateSpec object as JSON/YAML. | | Type: object
Optional: \{\}
| +| `config` _[vmcp.config.Config](#vmcpconfigconfig)_ | Config is the Virtual MCP server configuration
The only field currently required within config is `config.groupRef`.
GroupRef references an existing MCPGroup that defines backend workloads.
The referenced MCPGroup must exist in the same namespace.
The telemetry and audit config from here are also supported, but not required. | | Type: object
Optional: \{\}
| +| `embeddingServerRef` _[api.v1alpha1.EmbeddingServerRef](#apiv1alpha1embeddingserverref)_ | EmbeddingServerRef references an existing EmbeddingServer resource by name.
When the optimizer is enabled, this field is required to point to a ready EmbeddingServer
that provides embedding capabilities.
The referenced EmbeddingServer must exist in the same namespace and be ready. | | Optional: \{\}
| + +#### api.v1alpha1.VirtualMCPServerStatus + +VirtualMCPServerStatus defines the observed state of VirtualMCPServer + +_Appears in:_ + +- [api.v1alpha1.VirtualMCPServer](#apiv1alpha1virtualmcpserver) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- | ------- | ----------------------------------------------------------------- | +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#condition-v1-meta) array_ | Conditions represent the latest available observations of the VirtualMCPServer's state | | Optional: \{\}
| +| `observedGeneration` _integer_ | ObservedGeneration is the most recent generation observed for this VirtualMCPServer | | Optional: \{\}
| +| `phase` _[api.v1alpha1.VirtualMCPServerPhase](#apiv1alpha1virtualmcpserverphase)_ | Phase is the current phase of the VirtualMCPServer | Pending | Enum: [Pending Ready Degraded Failed]
Optional: \{\}
| +| `message` _string_ | Message provides additional information about the current phase | | Optional: \{\}
| +| `url` _string_ | URL is the URL where the Virtual MCP server can be accessed | | Optional: \{\}
| +| `discoveredBackends` _[api.v1alpha1.DiscoveredBackend](#apiv1alpha1discoveredbackend) array_ | DiscoveredBackends lists discovered backend configurations from the MCPGroup | | Optional: \{\}
| +| `backendCount` _integer_ | BackendCount is the number of healthy/ready backends
(excludes unavailable, degraded, and unknown backends) | | Optional: \{\}
| + +#### api.v1alpha1.Volume + +Volume represents a volume to mount in a container + +_Appears in:_ + +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) + +| Field | Description | Default | Validation | +| -------------------- | ----------------------------------------------------------------- | ------- | --------------------- | +| `name` _string_ | Name is the name of the volume | | Required: \{\}
| +| `hostPath` _string_ | HostPath is the path on the host to mount | | Required: \{\}
| +| `mountPath` _string_ | MountPath is the path in the container to mount to | | Required: \{\}
| +| `readOnly` _boolean_ | ReadOnly specifies whether the volume should be mounted read-only | false | Optional: \{\}
| diff --git a/versioned_docs/version-1.0/toolhive/reference/index.mdx b/versioned_docs/version-1.0/toolhive/reference/index.mdx new file mode 100644 index 00000000..15c72cb5 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/index.mdx @@ -0,0 +1,79 @@ +--- +title: Reference +description: + Technical reference documentation for ToolHive, including CLI commands, API + specifications, CRD definitions, and registry schemas. +displayed_sidebar: toolhiveSidebar +--- + +import DocCard from '@theme/DocCard'; + +Technical reference documentation for all ToolHive components. For guides and +tutorials, see the individual product sections. + +
+ + + + + + + + + + + + + + + +
diff --git a/versioned_docs/version-1.0/toolhive/reference/registry-api.mdx b/versioned_docs/version-1.0/toolhive/reference/registry-api.mdx new file mode 100644 index 00000000..f40ae7be --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/registry-api.mdx @@ -0,0 +1,10 @@ +--- +title: Registry Server API reference +sidebar_label: API reference +description: Reference documentation for the ToolHive Registry Server REST API +hide_table_of_contents: true +--- + +import ApiDocMdx from '@theme/ApiDocMdx'; + + diff --git a/versioned_docs/version-1.0/toolhive/reference/registry-schema-toolhive.mdx b/versioned_docs/version-1.0/toolhive/reference/registry-schema-toolhive.mdx new file mode 100644 index 00000000..d955e5e6 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/registry-schema-toolhive.mdx @@ -0,0 +1,34 @@ +--- +title: ToolHive registry JSON schema +description: The JSON schema for the ToolHive registry. +displayed_sidebar: toolhiveSidebar +--- + +import JSONSchemaViewer from '@theme/JSONSchemaViewer'; +import Schema from '@site/static/api-specs/toolhive-legacy-registry.schema.json'; + +This is the JSON schema for the ToolHive-native registry format. It defines the +structure and constraints for registry entries, ensuring that all entries +conform to a consistent format. + +:::info + +This format is considered legacy now that an +[official upstream registry format](registry-schema-upstream.mdx) exists. +ToolHive supports both formats, and the ToolHive-native format continues to work +for the built-in registry, custom file-based registries, and the ToolHive +Registry Server. + +::: + +To use this schema in your own custom registry file, add a `$schema` property at +the top of your JSON file: + +```json +{ + "$schema": "https://raw.githubusercontent.com/stacklok/toolhive-core/main/registry/types/data/toolhive-legacy-registry.schema.json", + ... +} +``` + + diff --git a/versioned_docs/version-1.0/toolhive/reference/registry-schema-upstream.mdx b/versioned_docs/version-1.0/toolhive/reference/registry-schema-upstream.mdx new file mode 100644 index 00000000..c9ba8314 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/reference/registry-schema-upstream.mdx @@ -0,0 +1,205 @@ +--- +title: Upstream registry JSON schema +description: The JSON schema for the official upstream MCP registry format. +displayed_sidebar: toolhiveSidebar +--- + +import JSONSchemaViewer from '@theme/JSONSchemaViewer'; +import RegistrySchema from '@site/static/api-specs/upstream-registry.schema.json'; +import McpServerSchema from '@site/static/api-specs/mcp-server.schema.json'; +import PublisherProvidedSchema from '@site/static/api-specs/publisher-provided.schema.json'; + +This is the JSON schema for the official upstream MCP registry format. It +defines the structure and constraints for registry entries, ensuring that all +entries conform to a consistent format. The registry wraps the official MCP +server schema, which is documented separately below. + +:::info + +ToolHive also supports the +[ToolHive-native registry format](./registry-schema-toolhive.mdx), which is used +by the built-in registry. Both formats work with custom file-based registries +and the ToolHive Registry Server. + +::: + +To use this schema in your own custom registry file, add a `$schema` property at +the top of your JSON file: + +```json +{ + "$schema": "https://raw.githubusercontent.com/stacklok/toolhive-core/main/registry/types/data/upstream-registry.schema.json", + ... +} +``` + +## Registry schema + + + +## MCP server object schema + +The `servers` array in the registry contains objects that conform to the +official MCP server schema: + + + +## ToolHive extensions schema + +ToolHive extends the upstream MCP server schema with additional metadata to +support features like container configuration, authentication, and +categorization. These extensions are stored in the +`_meta['io.modelcontextprotocol.registry/publisher-provided']` property of each +server definition. + +The available properties vary by server type: + +- **Container servers** (keyed by OCI image reference): `permissions`, `args`, + `provenance`, `docker_tags`, and `proxy_port` +- **Remote servers** (keyed by URL): `oauth_config` and `env_vars` +- **All servers**: `status`, `tier`, `tools`, `tags`, `metadata`, and + `custom_metadata` + +:::note Read-only metadata + +The `metadata` object can include an optional `kubernetes` nested object with +fields like `kind`, `namespace`, `name`, `uid`, `image`, and `transport`. This +metadata is automatically added by the ToolHive Registry Server for servers that +were auto-discovered from Kubernetes deployments (typically remote servers +accessed via Kubernetes-exposed URLs). You should not manually add this field +when creating custom registry files. + +::: + + + +## Full example + +The following example shows a complete custom registry file with both a +container server and a remote server, demonstrating the different ToolHive +extensions you can use when building your own registry: + +```json title="Example registry file with ToolHive extensions" +{ + "$schema": "https://raw.githubusercontent.com/stacklok/toolhive-core/main/registry/types/data/upstream-registry.schema.json", + "version": "1.0.0", + "meta": { + "last_updated": "2026-02-05T16:59:49Z" + }, + "data": { + "servers": [ + { + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", + "name": "io.github.stacklok/osv", + "description": "OSV (Open Source Vulnerabilities) database access for querying package and commit vulnerabilities", + "title": "osv", + "repository": { + "url": "https://github.com/StacklokLabs/osv-mcp", + "source": "github" + }, + "version": "1.0.0", + // highlight-next-line + "packages": [ + { + "registryType": "oci", + "identifier": "ghcr.io/stackloklabs/osv-mcp/server:0.1.0", + "transport": { + "type": "streamable-http", + "url": "http://localhost:8080" + } + } + ], + // highlight-start + "_meta": { + "io.modelcontextprotocol.registry/publisher-provided": { + "io.github.stacklok": { + "ghcr.io/stackloklabs/osv-mcp/server:0.1.0": { + // highlight-end + "metadata": { + "last_updated": "2026-01-30T02:55:46Z", + "pulls": 0, + "stars": 0 + }, + "permissions": { + "network": { + "outbound": { + "allow_host": ["api.osv.dev"], + "allow_port": [443] + } + } + }, + "provenance": { + "sigstore_url": "tuf-repo-cdn.sigstore.dev", + "repository_uri": "https://github.com/StacklokLabs/osv-mcp", + "signer_identity": "/.github/workflows/release.yml", + "runner_environment": "github-hosted", + "cert_issuer": "https://token.actions.githubusercontent.com" + }, + "status": "Active", + "tags": ["vulnerability", "security", "osv"], + "tier": "Community", + "tools": [ + "query_vulnerability", + "query_vulnerabilities_batch", + "get_vulnerability" + ] + } + } + } + } + }, + { + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", + "name": "io.github.stacklok/toolhive-doc-mcp-remote", + "description": "Search ToolHive docs for help with using and contributing to the project (hosted version)", + "title": "toolhive-doc-mcp-remote", + "repository": { + "url": "https://github.com/StacklokLabs/toolhive-doc-mcp", + "source": "github" + }, + "version": "1.0.0", + // highlight-next-line + "remotes": [ + { + "type": "streamable-http", + "url": "https://toolhive-doc-mcp.stacklok.dev/mcp" + } + ], + // highlight-start + "_meta": { + "io.modelcontextprotocol.registry/publisher-provided": { + "io.github.stacklok": { + "https://toolhive-doc-mcp.stacklok.dev/mcp": { + // highlight-end + "custom_metadata": { + "author": "Stacklok", + "homepage": "https://github.com/StacklokLabs/toolhive-doc-mcp", + "license": "Apache-2.0" + }, + "metadata": { + "last_updated": "2026-01-25T13:39:45Z", + "pulls": 0, + "stars": 3 + }, + "status": "Active", + "tags": [ + "remote", + "docs", + "documentation", + "help", + "search", + "stacklok", + "support", + "toolhive" + ], + "tier": "Official", + "tools": ["query_docs", "get_chunk"] + } + } + } + } + } + ] + } +} +``` diff --git a/versioned_docs/version-1.0/toolhive/support.mdx b/versioned_docs/version-1.0/toolhive/support.mdx new file mode 100644 index 00000000..31c477cc --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/support.mdx @@ -0,0 +1,117 @@ +--- +title: Getting support +sidebar_label: Support +description: How to get help and support for ToolHive +--- + +Whether you're troubleshooting an issue, looking for guidance, or need help with +your ToolHive deployment, there are several ways to get support. This page +outlines the available resources to help you find answers quickly. + +## Self-help resources + +### Documentation + +This documentation site is your first stop for help. You can search using +keywords or ask questions using AI-powered search: + +- **Keyword search**: Use the search bar at the top of the page to find specific + topics, commands, or configuration options. +- **AI search**: Type a question in the search and select the **Ask AI** option + to get relevant answers from the documentation. + +### ToolHive documentation MCP server + +For an enhanced documentation experience, you can use the ToolHive documentation +search MCP server. This server provides access to the ToolHive documentation +directly within your AI-powered tools, making it easy to get context-aware help +while you work. + +ToolHive offers two ways to access the documentation server: + +- **`toolhive-doc-mcp-remote`** (recommended): A hosted remote MCP server that's + always up-to-date with the latest documentation. +- **`toolhive-doc-mcp`**: A containerized local version you can run on your + machine. + +Both versions are available in the ToolHive registry. The +[`toolhive-doc-mcp`](https://github.com/StacklokLabs/toolhive-doc-mcp) source +code is available on GitHub. + +:::tip + +Once you've added the ToolHive documentation MCP server, you can ask your AI +assistant questions like "How do I configure ToolHive?" or "What are the +available commands?" and get answers directly from the documentation. + +::: + +#### Using with the ToolHive UI + +1. Open the ToolHive UI application. +2. Open the **Registry** page. +3. Search for `toolhive-doc` to find both versions. +4. Select `toolhive-doc-mcp-remote` (recommended) or `toolhive-doc-mcp` (local). +5. Review the pre-filled configuration and click **Install server** to start the + MCP server. + +The server will appear on the MCP Servers page with a "Running" status. The +documentation is now available to your connected AI clients. + +See the [Run MCP servers](./guides-ui/run-mcp-servers.mdx) guide for more +details. + +#### Using with the ToolHive CLI + +To run the hosted remote version (recommended): + +```bash +thv run toolhive-doc-mcp-remote +``` + +Or to run the local containerized version: + +```bash +thv run toolhive-doc-mcp +``` + +Once started, the documentation will be available to any AI clients registered +with ToolHive. To verify the server is running: + +```bash +thv list +``` + +See the [Run MCP servers](./guides-cli/run-mcp-servers.mdx) guide for more +details. + +## Community support + +### Discord + +Join the [Stacklok Discord community](https://discord.gg/stacklok) to connect +with the ToolHive community. The `#user-chat` channel is the place to ask usage +questions, share tips, and get help from other users and the Stacklok team. + +You can also ask questions to the `@stacklok-bot` chatbot in Discord. The bot +can help answer common questions and provide guidance on using ToolHive. + +### GitHub issues + +If you've found a bug or want to request a new feature, please open an issue on +the [ToolHive GitHub repository](https://github.com/stacklok/toolhive). When +reporting a bug, include as much detail as possible, such as: + +- Your operating system and version +- The version of ToolHive you're using +- Steps to reproduce the issue +- Any error messages or logs + +This information helps the team diagnose and fix issues more quickly. + +## Enterprise support + +For organizations that need an enterprise distribution of ToolHive with +dedicated support, custom integrations, or assistance with large-scale +deployments, see the [Stacklok Enterprise](./enterprise.mdx) page to learn more +and get in touch. diff --git a/versioned_docs/version-1.0/toolhive/tutorials/custom-registry.mdx b/versioned_docs/version-1.0/toolhive/tutorials/custom-registry.mdx new file mode 100644 index 00000000..ac38b280 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/tutorials/custom-registry.mdx @@ -0,0 +1,375 @@ +--- +title: Create a custom MCP registry +description: Learn how to create a custom MCP registry for ToolHive. +--- + +## Overview + +ToolHive includes a built-in registry of MCP servers with verified +configurations that meet a +[minimum quality standard](../concepts/registry-criteria.mdx). + +But you can also create your own custom registry to include the MCP servers that +are relevant to your organization or specific use cases. This allows you to +curate a list of servers that meet your specific needs. + +## Why create a custom registry? + +Creating a custom registry allows you to: + +- Curate a list of MCP servers tailored to your organization's needs +- Include private or internal servers not listed in the public registry +- Pre-configure server settings for easier deployment +- Ensure all servers meet your organization's quality and security standards + +## Create your first custom registry + +In this tutorial, you'll create a custom MCP registry for ToolHive and configure +it to use your own curated list of MCP servers. By the end, you'll have a +working custom registry that you can extend with your organization's specific +MCP servers. + +### What you'll build + +You'll create a JSON registry file containing a simple MCP server entry, +configure ToolHive to use your custom registry, and verify it works by listing +and running servers from your registry. + +### Prerequisites + +Before you start, make sure you have: + +- The ToolHive UI or CLI installed and working on your system + - [ToolHive UI quickstart](../guides-ui/quickstart.mdx) + - [ToolHive CLI quickstart](../guides-cli/quickstart.mdx) +- Basic familiarity with JSON format +- A text editor for creating the registry file + +### Step 1: Create the registry file + +First, create a new directory for your custom registry and navigate to it: + +```bash +mkdir my-custom-registry +cd my-custom-registry +``` + +Create a new file called `registry.json` and add the following content: + +```json title='registry.json' +{ + "$schema": "https://raw.githubusercontent.com/stacklok/toolhive-core/main/registry/types/data/toolhive-legacy-registry.schema.json", + "version": "1.0.0", + "last_updated": "2025-08-15T10:00:00Z", + "servers": { + "my-fetch": { + "description": "A custom web content fetching MCP server for our organization", + "image": "ghcr.io/stackloklabs/gofetch/server:latest", + "status": "Active", + "tier": "Community", + "transport": "streamable-http", + "args": ["--user-agent", "Mozilla/5.0 (compatible;MyOrgFetchBot/1.0)"], + "tags": ["web", "fetch", "content"], + "tools": ["fetch"], + "permissions": { + "network": { + "outbound": { + "allow_host": [".example.com", "api.mycompany.com"], + "allow_port": [80, 443] + } + } + } + } + }, + "groups": [ + { + "name": "dev-toolkit", + "description": "Essential MCP servers for development workflows", + "servers": { + "fetch-tool": { + "description": "A web content fetching MCP server for development", + "image": "ghcr.io/stackloklabs/gofetch/server:latest", + "status": "Active", + "tier": "Community", + "transport": "streamable-http", + "args": ["--user-agent", "Mozilla/5.0 (compatible;DevBot/1.0)"], + "tags": ["web", "fetch", "dev"], + "tools": ["fetch"], + "permissions": { + "network": { + "outbound": { + "allow_host": [".github.com", ".stackoverflow.com"], + "allow_port": [80, 443] + } + } + } + } + } + } + ] +} +``` + +This registry contains two types of server configurations: + +- A standalone MCP server called `my-fetch` in the top-level `servers` section +- A group called `dev-toolkit` containing a `fetch-tool` server + +The `dev-toolkit` group demonstrates how you can organize related servers +together. When you run this group later, you'll be able to start multiple +servers as a single unit—perfect for development workflows or team-specific +toolsets. Notice how the `fetch-tool` server has a different user agent and +network permissions than `my-fetch`, showing how you can customize servers +within groups for specific purposes. + +Your server configurations include everything ToolHive needs to run them: + +- A description explaining its purpose +- The container image to use +- Required metadata like status and tier +- A list of tools it provides +- Command-line arguments for customization +- Network permissions for security + +:::note + +Registry groups are supported in both the CLI and the ToolHive UI. + +::: + +### Step 2: Configure ToolHive to use your registry + +Configure ToolHive to use your custom registry. + + + + +1. Open the ToolHive application +2. Navigate to **Settings** → **Registry** +3. Select the **Local Registry** option +4. Enter the full path to your `registry.json` file (for example: + `/Users//my-custom-registry/registry.json`) +5. Click **Save** to apply the configuration + +The UI will validate the registry file and confirm it's been set successfully. + + + + +Set the registry file using the full path: + +```bash +thv config set-registry /Users//my-custom-registry/registry.json +``` + +Verify the configuration was applied: + +```bash +thv config get-registry +``` + +You should see the path to your registry file displayed. + + + + +### Step 3: Test your custom registry + +Verify your custom registry is working. + + + + +1. Navigate to the **Registry** page from the menu bar +2. You should see your `my-fetch` server displayed alongside any other servers + in your custom registry +3. Click on the server to view its details, including description, tools, and + configuration options + + + + +List the servers in your custom registry: + +```bash +thv registry list +``` + +You should see your `my-fetch` server listed. Get detailed information about it: + +```bash +thv registry info my-fetch +``` + +This displays the server's configuration, tools, and permissions as defined in +your registry. + + + + +### Step 4: Run a server from your registry + +Finally, run the MCP server from your custom registry. + + + + +1. On the **Registry** page, click on your `my-fetch` server +2. Click the **Install server** button +3. Configure any required settings (the defaults from your registry will be + pre-populated) +4. Click **Install server** to start the MCP server +5. Navigate to the **MCP Servers** page to see your running server and manage it + +The server will appear in your MCP servers list, where you can start, stop, view +logs, and manage it like any other MCP server. + +To install the `dev-toolkit` group, click on it in the registry grid and then +click **Install group**. The installation wizard guides you through configuring +each server in the group one at a time. For more details on installing groups, +see [Explore the registry](../guides-ui/registry.mdx#registry-groups). + + + + +```bash +thv run my-fetch +``` + +The server should start successfully, demonstrating that your custom registry is +working correctly. + +Next, run the `dev-toolkit` group: + +```bash +thv group run dev-toolkit +``` + +This starts all servers in the `dev-toolkit` group (in this case, just the +`fetch-tool` server). + + + + +### Step 5: Add more servers (optional) + +You can extend your registry by adding more servers to the `servers` object. For +example, add a second server (note this is just an example, it will not function +if you try to run it): + +```json title='registry.json' +{ + "$schema": "https://raw.githubusercontent.com/stacklok/toolhive-core/main/registry/types/data/toolhive-legacy-registry.schema.json", + "version": "1.0.0", + "last_updated": "2025-08-15T10:00:00Z", + "servers": { + "my-fetch": { + // ... existing server configuration + }, + "company-tools": { + "description": "Internal company tools and utilities MCP server", + "image": "registry.company.com/mcp/company-tools:v1.2.0", + "status": "Active", + "tier": "Community", + "transport": "stdio", + "tools": ["get_employee_info", "create_ticket", "check_inventory"], + "tags": ["internal", "company", "tools"], + "env_vars": [ + { + "name": "COMPANY_API_KEY", + "description": "API key for accessing company internal services", + "required": true, + "secret": true + } + ] + } + } +} +``` + +After updating the file, the new server is immediately available for ToolHive +CLI commands. For the UI, navigate to the registry settings and click **Save** +to see the new server listed. + +## Production considerations + +While this tutorial uses a local file for simplicity, in production environments +you should: + +- **Host your registry on a secure HTTP server** +- **Use version control** to track changes to your registry +- **Implement proper access controls** to prevent unauthorized modifications +- **Set up automated validation** to ensure registry entries follow the schema +- **Regularly update** the registry with new servers and remove deprecated ones + +For production use, configure ToolHive to use a remote registry URL: + +```bash +thv config set-registry https://registry.example.com/mcp-registry.json +``` + +## What you've learned + +You've successfully: + +- Created a custom MCP registry following the JSON schema +- Configured ToolHive to use your custom registry +- Added MCP servers with proper metadata and permissions +- Created groups to organize related servers +- Tested your registry by listing and running both individual servers and groups + +You can now maintain your own curated list of MCP servers and groups tailored to +your organization's needs, organized into logical groups for different teams or +workflows. The registry can include both public servers from container +registries and private servers hosted within your organization. + +## Next steps + +- Explore the full [schema reference](../reference/registry-schema-toolhive.mdx) + to understand all available configuration options +- Learn about [custom permissions](../guides-cli/custom-permissions.mdx) for + fine-grained security control +- Set up [secrets management](../guides-cli/secrets-management.mdx) for servers + requiring API keys + +## Cleanup: Revert to the default registry + +If you want to switch back to using ToolHive's built-in registry after +completing this tutorial, you can easily revert your configuration. + + + + +To revert to the default registry through the UI: + +1. Navigate to **Settings** → **Registry** +2. Select the **Default Registry** option +3. Click **Save** to apply the configuration + +The UI will confirm that you're now using the built-in ToolHive registry. + + + + +To revert to the default registry using the CLI: + +```bash +thv config unset-registry +``` + +Verify that you're back to using the default registry: + +```bash +thv config get-registry +``` + +You should see a message indicating that the built-in registry is being used. + + + + +After reverting, all registry commands +([`thv registry list`](../reference/cli/thv_registry_list.md), +[`thv registry info`](../reference/cli/thv_registry_info.md), +[`thv search`](../reference/cli/thv_search.md)) will use ToolHive's built-in +registry instead of your custom one. diff --git a/versioned_docs/version-1.0/toolhive/tutorials/mcp-optimizer.mdx b/versioned_docs/version-1.0/toolhive/tutorials/mcp-optimizer.mdx new file mode 100644 index 00000000..021a51c0 --- /dev/null +++ b/versioned_docs/version-1.0/toolhive/tutorials/mcp-optimizer.mdx @@ -0,0 +1,332 @@ +--- +title: Reduce token usage with MCP Optimizer +description: Enable the MCP Optimizer to enhance tool selection and reduce token usage. +--- + +## Overview + +The ToolHive MCP Optimizer acts as an intelligent intermediary between AI +clients and MCP servers. It provides tool discovery, unified access to multiple +MCP servers through a single endpoint, and intelligent routing of requests to +appropriate MCP tools. + +:::info[Status] + +The MCP Optimizer is currently experimental. If you try it out, please share +your feedback on the [Stacklok Discord community](https://discord.gg/stacklok). + +::: + +## About MCP Optimizer + +### Benefits + +- **Reduced token usage**: Narrow down the toolset to only relevant tools for a + given task, minimizing context overload and token consumption +- **Improved tool selection**: Find the most appropriate tools across all + connected MCP servers +- **Simplified client configuration**: Connect to a single MCP Optimizer + endpoint instead of managing multiple MCP server connections + +### How it works + +Instead of flooding the model with all available tools, MCP Optimizer introduces +two lightweight primitives: + +1. `find_tool`: Searches for the most relevant tools using hybrid semantic + + keyword search +2. `call_tool`: Routes the selected tool request to the appropriate MCP server + +The workflow is as follows: + +1. You send a prompt that requires tool assistance (for example, interacting + with a GitHub repo) +2. The assistant calls `find_tool` with relevant keywords extracted from the + prompt +3. MCP Optimizer returns the most relevant tools (up to 8 by default, but this + is configurable) +4. Only those tools and their descriptions are included in the context sent to + the model +5. The assistant uses `call_tool` to execute the task with the selected tool + +```mermaid +flowchart TB + subgraph optimizerGroup["MCP Optimizer group (internal)"] + direction TB + optimizer["MCP Optimizer"] + end + subgraph target["ToolHive group: default"] + direction TB + mcp1["MCP server"] + mcp2["MCP server"] + mcp3["MCP server"] + end + + client(["Client"]) <-- connects --> optimizerGroup + optimizer <-. discovers/routes .-> target +``` + +## Prerequisites + +- One of the following container runtimes: + - macOS: Docker Desktop, Podman Desktop, or Rancher Desktop (using dockerd) + - Windows: Docker Desktop or Rancher Desktop (using dockerd) + - Linux: Not currently supported +- ToolHive UI version 0.12.0 or later + +## Step 1: Install MCP servers in a ToolHive group + +Before you can use MCP Optimizer, you need to have one or more MCP servers +running in a ToolHive group. If you don't have any MCP servers set up yet, +follow these steps: + + + + +1. Open the ToolHive UI and go to the **MCP Servers** screen +2. Ensure you're in the `default` group (or create a new group if desired) +3. Run one or more MCP servers by clicking the **Add an MCP Server** button and + selecting from the registry or using a custom server image + + For this tutorial, you can run the following example MCP servers: + - `github`: Provides tools for interacting with GitHub repositories + ([guide](../guides-mcp/github.mdx?mode=ui)) + - `fetch`: Provides a web search tool to fetch recent news articles + - `time`: Provides a tool to get the current time in various time zones + +4. Wait for the MCP servers to start and become healthy + +See the [Run MCP servers](../guides-ui/run-mcp-servers.mdx) guide for more +details. + + + + +Run one or more MCP servers in the `default` group using the ToolHive CLI. For +this tutorial, you can run the following example MCP servers: + +- `github`: Provides tools for interacting with GitHub repositories + ([guide](../guides-mcp/github.mdx?mode=cli)) +- `fetch`: Provides a web search tool to fetch recent news articles +- `time`: Provides a tool to get the current time in various time zones + +```bash +thv run github +thv run fetch +thv run time +``` + +See the [Run MCP servers](../guides-cli/run-mcp-servers.mdx) guide for more +details. + +Verify the MCP servers are running: + +```bash +thv list +``` + + + + +## Step 2: Connect your AI client + +Connect your AI client to the ToolHive group where the MCP servers are running +(for example, the `default` group). + +:::note + +For best results, connect your client to only the optimized group. If you +connect it to multiple groups, ensure there is no overlap in MCP servers between +the groups to avoid unpredictable behavior. + +::: + + + + +Open the ToolHive UI and use the **Manage Clients** button on the main MCP +Servers screen to register your AI client with the appropriate group (for +example, `default`). + +See the [Client configuration](../guides-ui/client-configuration.mdx) guide for +more details. + + + + +Run the following command to register your AI client with the ToolHive group +where the MCP servers are running (for example, `default`): + +```bash +thv client setup +``` + +See the [Client configuration](../guides-cli/client-configuration.mdx) guide for +more details. + + + + +Open your AI client and verify that it is connected to the correct MCP servers. +If you installed the `github`, `fetch`, and `time` servers, you should see +almost 50 tools available. + +## Step 3: Enable MCP Optimizer + + + + +The ToolHive UI automates the setup of the MCP Optimizer. + +1. Open the **Settings** (⚙️) screen and enable **MCP Optimizer** under + **Experimental Features** +2. Click the **Configure** button on the notification that pops up, or go to the + main **MCP Servers** screen and click **MCP Optimizer** in the left sidebar +3. Select the group that contains the MCP servers you want to optimize and click + **Apply Changes** + +ToolHive automatically updates clients that were registered with the selected +group to use the MCP Optimizer. If you want to connect a new client, go to the +`default` group and use the **Manage Clients** button to register it. + +Open your AI client and check its MCP configuration. You should see a single MCP +server named `toolhive-mcp-optimizer`. + +:::info[What's happening?] + +When you enable MCP Optimizer, ToolHive automatically creates an internal group +and runs the `mcp-optimizer` MCP server in that group. + +The MCP Optimizer server discovers the MCP servers in the selected group and +builds an index of their tools for intelligent routing. Automatic polling keeps +the index up to date as servers are added or removed from the optimized group. + +ToolHive also disconnects your AI clients from the original MCP server group and +reconnects them to the MCP Optimizer group. + +::: + + + + +The ToolHive UI is the recommended way to set up MCP Optimizer, but you can also +do it manually with the ToolHive CLI. + +**Step 3.1: Run the API server** + +MCP Optimizer uses the ToolHive API server to discover MCP servers and manage +client connections. + +You can run the API server in two ways. The simplest is to install and run the +ToolHive UI, which automatically starts the API server in the background. + +If you prefer to run the API server manually using the CLI, open a dedicated +terminal window and start it on a specific port: + +```bash +thv serve --port 50100 +``` + +Note the port number (`50100` in this example) for use in the next step. + +**Step 3.2: Create a dedicated group and run mcp-optimizer** + +```bash +# Create the meta group +thv group create optimizer + +# Run mcp-optimizer in the dedicated group +thv run --group optimizer -e TOOLHIVE_PORT=50100 mcp-optimizer +``` + +If you are running the API server using the ToolHive UI, omit the +`TOOLHIVE_PORT` environment variable. + +**Step 3.3: Configure your AI client for the meta group** + +Remove your client from the `default` group. For example, to unregister Cursor: + +```bash +thv client remove cursor --group default +``` + +Then, register your client with the `optimizer` group: + +```bash +# Run the group setup, select the optimizer group, and then select your client +thv client setup + +# Verify the configuration +thv client list-registered +``` + +:::note + +Your client now connects only to the `optimizer` group and sees only the +`mcp-optimizer` MCP server. + +::: + +The resulting configuration should look like this: + +```mermaid +flowchart TB + subgraph meta["ToolHive group: optimizer"] + direction TB + optimizer["mcp-optimizer"] + end + subgraph def["ToolHive group: default"] + direction TB + mcp1["github"] + mcp2["fetch"] + mcp3["time"] + end + + client(["Client"]) <-- connects --> meta + optimizer <-. discovers/routes .-> def + client x-. 🚫 .-x def +``` + + + + +## Step 4: Sample prompts + +After you configure and run MCP Optimizer, you can use the same prompts you +would normally use with individual MCP servers. The Optimizer automatically +discovers and routes to appropriate tools. + +Using the example MCP servers above, here are some sample prompts: + +- "Get the details of GitHub issue 1911 from the stacklok/toolhive repo" +- "List recent PRs from the stacklok/toolhive repo" +- "Fetch the latest news articles about AI" +- "What is the current time in Tokyo?" + +Watch how MCP Optimizer intelligently selects and routes to the relevant tools +across the connected MCP servers, reducing token usage and improving response +quality. + +To check your token savings, you can ask the optimizer: + +- "How many tokens did I save using MCP Optimizer?" + +## What's next? + +Now that you've set up MCP Optimizer, consider exploring these next steps: + +- Experiment with different MCP servers to see how MCP Optimizer enhances tool + selection and reduces token usage +- Monitor the performance and effectiveness of MCP Optimizer in your AI + applications +- Use the [ToolHive Playground](../guides-ui/playground.mdx) to test and refine + your prompts with MCP Optimizer +- Provide feedback on your experience with MCP Optimizer on the + [Stacklok Discord community](https://discord.gg/stacklok) + +## Related information + +- [MCP Optimizer UI guide](../guides-ui/mcp-optimizer.mdx) +- [Optimize tool discovery in vMCP](../guides-vmcp/optimizer.mdx) — Kubernetes + operator approach +- [Organize MCP servers into groups](../guides-ui/group-management.mdx) diff --git a/versioned_docs/version-1.1/theme-preview.mdx b/versioned_docs/version-1.1/theme-preview.mdx new file mode 100644 index 00000000..67de14ff --- /dev/null +++ b/versioned_docs/version-1.1/theme-preview.mdx @@ -0,0 +1,314 @@ +--- +title: Theme preview page +description: This is a page used to preview a lot of theme elements in one place. +displayed_sidebar: toolhiveSidebar +pagination_next: toolhive/guides-cli/install +pagination_prev: toolhive/index +--- + + + + + +This is a page used to preview a lot of theme elements in one place when working +on styles. + +[Community knowledge base for Docusaurus design tips](https://docusaurus.community/knowledge/design/) + +Breadcrumbs above aren't very interesting since this is a top-level page that +doesn't participate in a sidebar, navigate to a different page to see them. + +Another element that can't be easily reproduced here is the DocCardList +component, but you can see it in action on the +[ToolHive CLI guides index page](./toolhive/guides-cli/index.mdx). + +## Level 2 heading + +By default level 2-3 headings generate the TOC on the top right +([reference](https://docusaurus.io/docs/markdown-features/toc#table-of-contents-heading-level)). + +### Level 3 heading + +Some text within a section. [Here is a link](/theme-preview.mdx). + +And here is some `inline code` to show how it looks, even some +[`inline code with a link`](/theme-preview.mdx). + +#### Level 4 heading + +This level won't appear in the TOC by default. + +## Code blocks + +[Docusaurus reference docs](https://docusaurus.io/docs/markdown-features/code-blocks) + +```js title="Some JavaScript with line numbers" showLineNumbers +console.log('We love marmots.'); + +function MarmotsAreGreat(agree) { + if (agree) { + // highlight-next-line + return 'I agree, and this line is highlighted!'; + } + + return 'I am wrong.'; +} +``` + +```yaml title="some-yaml.yaml" +--- +# Sample profile for validating repositories +version: v1 +type: profile +name: acme-github-profile +display_name: Sample Profile +alert: 'off' +remediate: 'off' +repository: + - type: allowed_selected_actions + def: + github_owned_allowed: true + verified_allowed: true + patterns_allowed: [] +``` + +```json title="Example JSON with highlighted lines" {2,5-7} +{ + "key": "String", + "Number": 1, + "array": [1, 2, 3], + "nested": { + "literals": true + } +} +``` + +## Admonitions + +These are MDX callouts +([reference](https://docusaurus.io/docs/markdown-features/admonitions)). + +To customize the title, use square brackets after the type, e.g. +`:::tip[My title]`. + +To keep Prettier from invalidating the admonition syntax, add empty lines around +the start and end of the admonition block (see +[here](https://docusaurus.io/docs/markdown-features/admonitions#usage-with-prettier)). + +They can be customized in src/css/custom.css like so +([reference](https://docusaurus.community/knowledge/design/admonitions/#updating-the-css)): + +```css +/* Customize the "Tip" admonition */ +.alert--success { + --ifm-alert-background-color: #59cfa8; + --ifm-alert-background-color-highlight: #00bbbe26; + --ifm-alert-foreground-color: #002a3e; + --ifm-alert-border-color: #002a3e; +} + +/* Use a different border color in dark mode */ +[data-theme='dark'] .alert--success { + --ifm-alert-border-color: #008385; +} +``` + +:::note + +This is a `note` admonition. Its CSS class is `theme-admonition-note`. + +[Here's a link inside the admonition](#tables). + +::: + +:::tip + +This is a `tip` admonition. Its CSS class is `theme-admonition-tip`. + +[Here's a link inside the admonition](#tables). + +::: + +:::info[Hello] + +This is an `info` admonition. Its CSS class is `theme-admonition-info` and it +has a custom title. + +[Here's a link inside the admonition](#tables). + +::: + +:::warning + +This is a `warning` admonition. Its CSS class is `theme-admonition-warning`. + +[Here's a link inside the admonition](#tables). + +::: + +:::danger + +This is a `danger` admonition. Its CSS class is `theme-admonition-danger`. + +[Here's a link inside the admonition](#tables). + +::: + +:::::info[Parent] + +Admonitions can be nested; example here so we can see how the colors look +together. + +::::danger[Child] + +Child content + +:::tip[Inception] + +This is getting silly + +::: + +:::: + +::::: + +## Tables + +A standard Markdown table: + +| Column 1 | Column 2 | Column 3 | +| --------------- | :---------: | ---------------------------- | +| This | hello | [A link in a table](#tables) | +| That | hi | `value` | +| The other thing | how are you | 🙈 | +| Another row | so you | can see the zebra effect | + +(Docusaurus theme enables header row and zebra rows by default) + +## Tabs + +MDX Tabs component, default theme +([reference](https://docusaurus.io/docs/markdown-features/tabs)) + + + + This is an apple 🍎 + + ```python title="Code block inside a tab" + print('We love marmots.') + + def marmots_are_great(agree): + if agree: + # highlight-next-line + return 'I agree, and this line is highlighted!' + + return 'I am wrong.' + ``` + + + + This is an orange 🍊 + + + This is a banana 🍌 + + + +## Details panel + +
+ Click to expand + +This is a details panel, which can be used to show additional information +without cluttering the page. + +It can contain any Markdown or MDX content, including `inline code`, code +blocks, images, even other details panels. +[Here's a link inside the details panel](#details-panel). + +```js title="JavaScript code inside a details panel" +console.log('This is inside a details panel.'); +``` + +
+ +## Images + +An MDX ThemedImage, which switches based on light/dark theme +([reference](https://docusaurus.io/docs/markdown-features/assets#themed-images)) + +import useBaseUrl from '@docusaurus/useBaseUrl'; +import ThemedImage from '@theme/ThemedImage'; + + + +An image using the "screenshot" CSS class to add a border and shadow/glow to +make them stand out from the background: + + + +## Diagrams + +A mermaid flowchart +([reference](https://docusaurus.io/docs/markdown-features/diagrams)) + +```mermaid +flowchart LR + node1["Node"] -- Label --> node2 + node2["Node"] -- Label --> node3 + subgraph container["**Subgraph**"] + direction LR + subgraph container1["Nested subgraph"] + node3["Node"] + end + end +``` + +A mermaid sequence diagram + +```mermaid +sequenceDiagram + Alice->>+John: Hello John, how are you? + note right of John: Note + John-->>-Alice: Great! + Alice-)John: See you later! +``` + +## Other standard elements + +Here's some **bold** and _italic_ text. + +Unordered list: + +- One +- Two +- Three + +Ordered list: + +1. One +1. Two +1. Three + +Horizontal line: + +--- + +## Pagination diff --git a/versioned_docs/version-1.1/toolhive/_partials/_auth-troubleshooting.mdx b/versioned_docs/version-1.1/toolhive/_partials/_auth-troubleshooting.mdx new file mode 100644 index 00000000..fb41b43e --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/_partials/_auth-troubleshooting.mdx @@ -0,0 +1,42 @@ +
+Authentication issues + +If clients can't authenticate: + +1. Check that the JWT token is valid and not expired +2. Verify that the audience and issuer match your configuration +3. Ensure the JWKS URL is accessible +4. Check the server logs for specific authentication errors: + + ```bash + # View logs + thv logs + + # Follow logs in real-time (like tail -f) + thv logs --follow + + # View proxy logs instead of container logs + thv logs --proxy + ``` + +
+ +
+Authorization issues + +If authenticated clients are denied access: + +1. Make sure your Cedar policies explicitly permit the specific action + (remember, default deny) +2. Check that the principal, action, and resource match what's in your policies + (including case and formatting) +3. Examine any conditions in your policies to ensure they're satisfied (for + example, required JWT claims or tool arguments) +4. Remember that Cedar uses a default deny policy—if no policy explicitly + permits an action, it will be denied + +**Troubleshooting tip:** If access is denied, check that your policies +explicitly permit the action. Cedar uses a default deny model—if no policy +matches, the request is denied. + +
diff --git a/versioned_docs/version-1.1/toolhive/_partials/_basic-cedar-config.mdx b/versioned_docs/version-1.1/toolhive/_partials/_basic-cedar-config.mdx new file mode 100644 index 00000000..86f6b33b --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/_partials/_basic-cedar-config.mdx @@ -0,0 +1,35 @@ +Create a JSON or YAML file with Cedar policies. This example demonstrates +several policy patterns: + +- Allow everyone to use the weather tool +- Restrict the admin_tool to a specific user (alice123) +- Role-based access: only users with the "premium" role can call any tool +- Attribute-based: allow the calculator tool only for add/subtract operations + +Here's an example in JSON format: + +```json +{ + "version": "1.0", + "type": "cedarv1", + "cedar": { + "policies": [ + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"weather\");", + "permit(principal == Client::\"alice123\", action == Action::\"call_tool\", resource == Tool::\"admin_tool\");", + "permit(principal, action == Action::\"call_tool\", resource) when { principal.claim_roles.contains(\"premium\") };", + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"calculator\") when { resource.arg_operation == \"add\" || resource.arg_operation == \"subtract\" };" + ], + "entities_json": "[]" + } +} +``` + +You can also define custom resource attributes in `entities_json` for per-tool +ownership or sensitivity labels. + +:::tip + +For more policy examples and advanced usage, see +[Cedar policies](../concepts/cedar-policies.mdx). + +::: diff --git a/versioned_docs/version-1.1/toolhive/_partials/_client-config-intro.mdx b/versioned_docs/version-1.1/toolhive/_partials/_client-config-intro.mdx new file mode 100644 index 00000000..0d0eeefb --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/_partials/_client-config-intro.mdx @@ -0,0 +1,17 @@ +ToolHive automatically configures supported AI clients to work with MCP servers. +This guide shows you how to set up and manage client configurations. + +## Understanding client configuration + +Before an AI application can use ToolHive MCP servers, it needs to know where to +find them. You can configure clients in two ways: + +1. **{props.term} a supported client**: {props.term} your client with ToolHive + so it automatically manages and updates the client's configuration as you + start, stop, and remove MCP servers. +2. **Manual configuration**: For clients that ToolHive doesn't directly support, + manually configure them to connect to ToolHive-managed MCP servers using the + SSE or Streamable HTTP protocol. + +For a complete list of supported clients and compatibility details, see the +[Client compatibility reference](../reference/client-compatibility.mdx). diff --git a/versioned_docs/version-1.1/toolhive/_partials/_oidc-prerequisites.mdx b/versioned_docs/version-1.1/toolhive/_partials/_oidc-prerequisites.mdx new file mode 100644 index 00000000..99e1cb66 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/_partials/_oidc-prerequisites.mdx @@ -0,0 +1,22 @@ +Before you begin, make sure you have: + +- ToolHive installed and working +- Basic familiarity with OAuth, OIDC, and JWT concepts +- An identity provider that supports OpenID Connect (OIDC), such as Google, + GitHub, Microsoft Entra ID (Azure AD), Okta, Auth0, or Kubernetes (for service + accounts) + +From your identity provider, you'll need: + +- Client ID +- Audience value +- Issuer URL +- JWKS URL (for key verification) + +ToolHive uses OIDC to connect to your existing identity provider, so you can +authenticate with your own credentials (for example, Google login) or with +service account tokens (for example, in Kubernetes). ToolHive never sees your +password, only signed tokens from your identity provider. + +For background on authentication, authorization, and Cedar policy examples, see +[Authentication and authorization framework](../concepts/auth-framework.mdx). diff --git a/versioned_docs/version-1.1/toolhive/_partials/_remote-mcp-auth-examples.mdx b/versioned_docs/version-1.1/toolhive/_partials/_remote-mcp-auth-examples.mdx new file mode 100644 index 00000000..af1b84c6 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/_partials/_remote-mcp-auth-examples.mdx @@ -0,0 +1,82 @@ +#### Remote MCP server with Auto-Discovered authentication + +**Auto-Discovered** authentication is the preferred approach for any MCP server +that supports it, as ToolHive handles all authentication setup automatically +with minimal configuration required. Notion's remote MCP server is one example +that supports this feature: + +1. Configuration settings: + - **Server name**: `notion-remote` + - **Server URL**: `https://mcp.notion.com/mcp` + - **Transport**: Streamable HTTP + - **Authorization method**: Auto-Discovered + - **Callback port**: `45673` (or any available port on your system) +1. When you install the server, ToolHive discovers the OAuth endpoints, + registers a new client, and handles the authentication process. +1. Your browser opens for authentication. After you authorize access, the remote + MCP server appears in your server list with a "Running" status. + +#### Remote MCP server with OAuth2 authentication + +GitHub's remote MCP server requires manual OAuth configuration. You'll need to +create a GitHub OAuth app and provide the details in ToolHive. + +First, create a GitHub OAuth app: + +1. Go to [GitHub Developer Settings](https://github.com/settings/developers) +1. Click **New OAuth App** +1. Fill in the application details: + - **Application name**: Choose a descriptive name (e.g., "ToolHive GitHub + MCP") + - **Homepage URL**: Your application's homepage or `http://localhost` + - **Authorization callback URL**: `http://localhost:45673/callback` (the port + number must match the **Callback port** you will enter in ToolHive) +1. Click **Register application** +1. Copy the **Client ID** and generate a **Client secret** for use in ToolHive + +Configure the remote MCP server in ToolHive: + +1. Configuration settings: + - **Server name**: `github-remote` + - **Server URL**: `https://api.githubcopilot.com/mcp/` + - **Transport**: Streamable HTTP + - **Authorization method**: OAuth 2.0 + - **Callback port**: `45673` (or any available port on your system) + - **Authorize URL**: `https://github.com/login/oauth/authorize` + - **Token URL**: `https://github.com/login/oauth/access_token` + - **Client ID**: Your GitHub OAuth app client ID (e.g., + `Og44jirLIaUgSiTDNGA3`) + - **Client secret**: Your GitHub OAuth app client secret (optional if PKCE is + enabled) + - **Scopes**: `repo,user:email` (comma-separated list of required + permissions) + - **PKCE**: Enable this option for enhanced security without requiring a + client secret +1. When you install the server, ToolHive opens your browser to authenticate with + GitHub and authorize the application. +1. After you authenticate successfully, the remote MCP server appears in your + server list with a "Running" status. + +#### Remote MCP server with OIDC authentication + +GitHub's remote MCP server also supports OIDC authentication, which provides +additional security features and standardized token handling. Use the same +GitHub OAuth app from the previous example. + +1. Fill in the configuration form: + - **Server name**: `github-remote` + - **Server URL**: `https://api.githubcopilot.com/mcp/` + - **Transport**: Streamable HTTP + - **Authorization method**: OIDC + - **Callback port**: `45673` (or any available port on your system) + - **Issuer URL**: `https://github.com/login/oauth` + - **Client ID**: Your GitHub OAuth app client ID (e.g., + `Og44jirLIaUgSiTDNGA3`) + - **Client secret**: Your GitHub OAuth app client secret (optional if PKCE is + enabled) + - **PKCE**: Enable this option for enhanced security without requiring a + client secret +1. When you install the server, ToolHive opens your browser to authenticate with + GitHub using OIDC. +1. After you authenticate successfully, the remote MCP server appears in your + server list with a "Running" status. diff --git a/versioned_docs/version-1.1/toolhive/concepts/auth-framework.mdx b/versioned_docs/version-1.1/toolhive/concepts/auth-framework.mdx new file mode 100644 index 00000000..c520593d --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/concepts/auth-framework.mdx @@ -0,0 +1,349 @@ +--- +title: Authentication and authorization +description: Understanding ToolHive's authentication and authorization framework concepts. +--- + +This document explains the concepts behind ToolHive's authentication and +authorization framework, which secures MCP servers by verifying client identity +and controlling access to resources. You'll learn how these systems work +together, the reasoning behind their design, and the benefits of this approach. + +:::info[Scope of this documentation] + +This documentation covers **client-to-MCP-server authentication**—how clients +authenticate to the MCP server itself. This is about securing access to the MCP +server's tools and resources. + +This is different from **MCP-server-to-backend authentication**, which involves +how the MCP server authenticates to external services or APIs it calls (for +example, a GitHub MCP server authenticating to the GitHub API). That topic is +covered in [Backend authentication](./backend-auth.mdx). + +::: + +## Understanding authentication vs. authorization + +When you secure MCP servers, you need to understand the strong separation +between two critical security concepts: + +- **Authentication (authN):** Verifying the identity of clients connecting to + your MCP server ("Who are you?") +- **Authorization (authZ):** Determining what actions authenticated clients are + allowed to perform ("What can you do?") + +You should always perform authentication first, using a trusted identity +provider, and then apply authorization rules to determine what the authenticated +identity can do. ToolHive helps you follow this best practice by acting as a +gateway in front of your MCP servers. This approach lets you use proven identity +systems for authentication, while keeping your authorization policies clear, +flexible, and auditable. You don't need to add custom authentication or +authorization logic to every server—ToolHive handles it for you, consistently +and securely. + +## Why ToolHive centralizes authentication + +The +[official MCP specification](https://modelcontextprotocol.io/docs/tutorials/security/authorization) +recommends OAuth 2.1-based authorization for HTTP transports, where each MCP +server acts as an OAuth resource server. In practice, this model creates +significant operational challenges: + +- **OAuth client registration burden:** OAuth 2.0 requires pre-registered + redirect URIs at each identity provider. Many providers—such as Google, + GitHub, and Atlassian—require manual registration of OAuth clients to obtain a + client ID and client secret. If each user client (for example, an IDE) were + its own OAuth client, the registration burden would be impractical at scale. +- **No federation with external services:** While token exchange (RFC 8693) and + federated identity providers work when the upstream service is in the same + trust domain as the MCP server or has an established trust relationship with + the identity provider, many MCP servers need to access external services like + GitHub, Google, or Atlassian APIs where no federation relationship exists. +- **Per-server implementation cost:** Each MCP server would need to implement + its own token validation and scope management, duplicating security-critical + logic across servers. + +ToolHive addresses the per-server implementation cost by centralizing +authentication and authorization in its proxy layer. You configure ToolHive with +your identity provider and write Cedar policies for fine-grained +authorization—individual MCP servers don't need to implement token validation or +scope management. + +The [embedded authorization server](#embedded-authorization-server) addresses +the remaining challenges. It exposes standard OAuth endpoints and handles the +full OAuth web flow, eliminating the client registration burden through Dynamic +Client Registration (DCR) and solving the federation gap by obtaining tokens +directly from external providers like GitHub or Atlassian. ToolHive delegates +authentication to the upstream provider and issues its own tokens, giving MCP +clients a spec-compliant OAuth experience while centralizing the complexity of +token acquisition and management. + +## Authentication framework + +ToolHive uses OAuth-based authentication with support for both OAuth 2.1 and +OpenID Connect (OIDC), enabling both JWT tokens and opaque token validation. +This lets you connect ToolHive to any OAuth 2.1 or OIDC-compliant identity +provider (IdP), such as Google, GitHub, Microsoft Entra ID (Azure AD), Okta, +Auth0, or even Kubernetes service accounts. ToolHive never handles your raw +passwords or credentials; instead, it relies on access tokens issued by your +trusted provider—either self-contained JWT tokens or opaque tokens validated +through token introspection. + +### Why use OAuth-based authentication? + +OAuth-based authentication provides several key advantages for securing MCP +servers: + +- **Standard and interoperable:** You can connect ToolHive to any OAuth 2.1 or + OIDC-compliant IdP without custom code, supporting both human users and + automated services. +- **Proven and secure:** Authentication is delegated to battle-tested identity + systems, which handle login UI, multi-factor authentication, and password + storage. +- **Decoupled identity management:** You can use your existing SSO/IdP + infrastructure, making onboarding and management seamless. +- **Flexible for users and services:** This authentication framework supports + both interactive user login (for example, Google sign-in) and + service-to-service authentication (for example, Kubernetes service account + tokens). + +### Real-world authentication scenarios + +Understanding how OAuth-based authentication works in practice helps you design +better security for your MCP servers: + +**User login via Google:** For example, you can run an MCP server that requires +authentication using your Google credentials. ToolHive delegates login to +Google, receives an access token (either a JWT or an opaque token), and +validates it to authenticate you. This means users get a familiar login +experience while you benefit from Google's security infrastructure. + +**Service-to-service auth with Kubernetes:** If you run a microservice in a +Kubernetes cluster, it can present its service account token (typically an OIDC +JWT) to ToolHive. ToolHive validates the token using the cluster's OIDC issuer +and JWKS endpoint, enabling secure, automated authentication for your internal +services. + +### Token-based authentication + +ToolHive supports two types of access tokens for authentication: + +**JWT tokens (JSON Web Tokens):** Self-contained tokens that include identity +information within the token itself. JWTs are validated locally using +cryptographic signatures and consist of three parts: + +1. **Header:** Metadata about the token +2. **Payload:** Claims about the entity (typically you or your service) +3. **Signature:** Ensures the token hasn't been altered + +**Opaque tokens:** Reference tokens that don't contain identity information +directly. ToolHive validates these tokens by querying the identity provider's +token introspection endpoint to retrieve the associated claims. + +ToolHive automatically detects the token type and uses the appropriate +validation method—attempting JWT validation first and falling back to token +introspection if needed. + +### Authentication flow + +The authentication process follows these steps: + +1. **Token acquisition:** You obtain an access token from your identity + provider. +2. **Token presentation:** You include the token in your requests to ToolHive + (typically in the Authorization header). +3. **Token validation:** ToolHive validates the token using either: + - **Local validation:** For JWT tokens, verifying the signature, expiration, + and claims using the provider's public keys (JWKS) + - **Remote validation:** For opaque tokens, querying the provider's token + introspection endpoint to verify the token and retrieve claims +4. **Identity extraction:** ToolHive extracts your identity information from the + validated token claims. + +```mermaid +flowchart TD + Client -->|Access Token| ToolHive + ToolHive -->|Validate Token| Identity_Provider[Identity Provider] + ToolHive -->|Evaluate Cedar Policy| Cedar_Authorizer[Cedar Authorizer] + Cedar_Authorizer -->|Permit| MCP_Server + Cedar_Authorizer -->|Deny| Denied[403 Forbidden] +``` + +### Embedded authorization server + +In the standard authentication flow described above, clients obtain tokens +independently from an external identity provider and present them to ToolHive +for validation. The embedded authorization server provides an alternative model +where ToolHive itself acts as an OAuth authorization server, retrieving tokens +from an upstream identity provider on behalf of clients. + +:::note + +The embedded authorization server is currently available only for Kubernetes +deployments using the ToolHive Operator. + +::: + +From the client's perspective, the embedded authorization server provides a +standard OAuth 2.0 experience: + +1. If the client is not yet registered, it registers via Dynamic Client + Registration (DCR), receiving a `client_id` and `client_secret`. +2. The client is directed to the ToolHive authorization endpoint. +3. ToolHive redirects the client to the upstream identity provider for + authentication (for example, signing in with GitHub or Atlassian). +4. ToolHive exchanges the authorization code for upstream tokens and issues its + own JWT to the client, signed with keys you configure. +5. The client includes this JWT as a `Bearer` token in the `Authorization` + header on subsequent requests. + +Behind the scenes, ToolHive stores the upstream tokens and uses them to +authenticate MCP server requests to external APIs. For the complete flow, +including token storage and forwarding, see +[Embedded authorization server](./backend-auth.mdx#embedded-authorization-server). + +For Kubernetes setup instructions, see +[Set up embedded authorization server authentication](../guides-k8s/auth-k8s.mdx#set-up-embedded-authorization-server-authentication). + +### Identity providers + +ToolHive can integrate with any provider that supports OAuth 2.1 or OIDC, +including: + +- Google +- GitHub +- Microsoft Entra ID (Azure AD) +- Okta +- Auth0 +- Kubernetes (service account tokens) + +These same providers work with both external token validation and the embedded +authorization server. For the embedded authorization server, the upstream +provider must support the OAuth 2.0 authorization code flow. + +### Token validation methods + +ToolHive supports multiple token validation methods to work with different +identity providers: + +- **JWT validation:** For providers that issue JWT tokens, ToolHive validates + tokens locally using the provider's JWKS endpoint. This verifies the token's + signature, expiration, and claims without calling the identity provider for + each request. +- **Token introspection:** For providers that issue opaque tokens, ToolHive + validates tokens by querying the provider's introspection endpoint. This + supports RFC 7662 (OAuth 2.0 Token Introspection), Google's tokeninfo API, and + GitHub's token validation API. + +ToolHive automatically detects the token type—it first attempts JWT validation, +and if that fails, it falls back to token introspection. This means you don't +need to configure which validation method to use; ToolHive handles it +automatically based on the token format. + +## Authorization framework + +After authentication, ToolHive enforces authorization using Amazon's Cedar +policy language. ToolHive acts as a gateway in front of MCP servers, handling +all authorization checks before requests reach the server logic. This means MCP +servers do not need to implement their own OAuth or custom authorization +logic—ToolHive centralizes and standardizes access control. + +### Why Cedar for authorization? + +Cedar provides several advantages for MCP server authorization: + +- **Expressive and flexible:** Cedar supports both role-based (RBAC) and + attribute-based (ABAC) access control patterns, letting you create policies + that match your security requirements. +- **Formally verified:** Cedar's design has been formally verified for safety + and security properties, which reduces the risk of policy bugs. +- **Human-readable:** Cedar policies use clear, declarative syntax that's easy + to read, write, and audit. +- **Policy enforcement point:** ToolHive blocks unauthorized requests before + they reach the MCP server, which reduces risk and simplifies server code. +- **Secure by default:** Authorization is explicit—if a request is not + explicitly permitted, it is denied. Deny rules take precedence over permit + rules (deny overrides). + +### Authorization components + +ToolHive's authorization framework consists of: + +1. **Cedar authorizer:** Evaluates Cedar policies to determine if a request is + authorized +2. **Authorization middleware:** Extracts information from MCP requests and uses + the Cedar Authorizer +3. **Configuration:** A JSON or YAML file that specifies the Cedar policies and + entities + +### Authorization flow + +When a request arrives at an MCP server with authorization enabled: + +1. The authentication middleware authenticates the client and adds token claims + to the request context +2. The authorization middleware extracts information from the request + (principal, action, resource, and any arguments) +3. The Cedar authorizer evaluates policies to determine if the request is + authorized +4. If authorized, the request proceeds; otherwise, a 403 Forbidden response is + returned + +```mermaid +flowchart TD + Client -->|Access Token| ToolHive + ToolHive -->|Validate Token| Auth_Middleware + Auth_Middleware -->|Extract Claims| Authz_Middleware + Authz_Middleware -->|Evaluate Cedar Policies| Cedar_Authorizer + Cedar_Authorizer -->|Permit| MCP_Server + Cedar_Authorizer -->|Deny| Denied[403 Forbidden] +``` + +## Security and operational benefits + +ToolHive's authentication and authorization approach provides several key +benefits: + +- **Separation of concerns:** Authentication and authorization are handled + independently, following security best practices. +- **Integration with existing systems:** Use your existing identity + infrastructure (SSO, IdPs, Kubernetes, etc.). +- **Centralized, flexible policy model:** Define precise, auditable access rules + in a single place—no need to modify MCP server code. +- **Secure by default:** Requests are denied unless explicitly permitted by + policy, with deny precedence for maximum safety. +- **Auditable and versionable:** Policies are clear, declarative, and can be + tracked in version control for compliance and review. +- **Developer and operator friendly:** ToolHive acts as a smart proxy, so you + don't need to implement complex OAuth or custom auth logic in every server. + +## Client authentication support + +While ToolHive provides a robust authentication and authorization framework for +MCP servers, authentication support varies across the MCP client ecosystem. + +### MCP client capabilities + +The MCP ecosystem includes numerous clients with varying levels of +authentication support. Authentication support is not universal. Some clients +focus primarily on local, unauthenticated MCP servers for development workflows, +while others provide enterprise-grade authentication for production deployments. + +When selecting an MCP client for authenticated workflows, look for clients that +support the MCP authentication standards, including OAuth 2.1 and +transport-level authentication mechanisms. + +ToolHive's OIDC-based authentication approach aligns with industry standards and +works with clients that support modern authentication protocols. As the MCP +ecosystem continues to mature, we expect authentication support to become more +standardized across clients. + +## Related information + +- For configuring the embedded authorization server in Kubernetes, see + [Embedded authorization server authentication](../guides-k8s/auth-k8s.mdx#set-up-embedded-authorization-server-authentication) +- For backend authentication concepts, see + [Backend authentication](./backend-auth.mdx) +- For detailed policy writing guidance, see + [Cedar policies](./cedar-policies.mdx) +- For a complete end-to-end example with Okta OIDC and role-based access + control, see [Role-based authorization with Okta](../integrations/okta.mdx) diff --git a/versioned_docs/version-1.1/toolhive/concepts/backend-auth.mdx b/versioned_docs/version-1.1/toolhive/concepts/backend-auth.mdx new file mode 100644 index 00000000..04efb279 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/concepts/backend-auth.mdx @@ -0,0 +1,477 @@ +--- +title: Backend authentication +description: + Understanding how MCP servers authenticate to external services using + ToolHive's backend authentication patterns, including static credentials, + token exchange, and the embedded authorization server. +--- + +This document explains how ToolHive helps MCP servers authenticate to +third-party APIs and backend services exposed through the MCP servers. You'll +learn about the backend authentication patterns ToolHive supports, why they +improve security and multi-tenancy, and how they simplify MCP server +development. + +:::info[Scope of this documentation] + +This documentation covers **MCP-server-to-backend authentication**—how MCP +servers authenticate to external services or APIs they call (for example, a +GitHub MCP server authenticating to the GitHub API). + +This is different from **client-to-MCP-server authentication**, which involves +how clients authenticate to the MCP server itself. That topic is covered in +[Authentication and authorization](./auth-framework.mdx). + +::: + +## The challenge of backend authentication + +The MCP specification focuses on authorization to the MCP server but doesn't +specify how an MCP server should authenticate to the services it exposes. This +is intentionally left to implementers, which makes sense from a specification +perspective but leaves MCP server developers without clear guidance. + +Many MCP servers today either embed static API keys or require custom +authentication code. This creates several problems: + +- **Security risks:** Long-lived credentials stored in configuration files or + environment variables can be compromised +- **Audit challenges:** When multiple users share a service account, you can't + trace individual actions +- **Multi-tenancy complexity:** Supporting multiple tenants with isolated + credentials requires significant custom code + +ToolHive addresses these challenges with three backend authentication patterns: +static credentials for services that don't support OAuth, token exchange for +services in the same or federated trust domain, and the embedded authorization +server for OAuth-based external APIs where no federation exists. + +## How ToolHive handles backend authentication + +ToolHive sits between clients and MCP servers, and can acquire backend +credentials on behalf of the MCP server. Depending on the pattern, it might +exchange the client's token, run an OAuth flow against an external provider, or +inject static credentials. In each case, the MCP server receives ready-to-use +credentials—via an `Authorization: Bearer` header, another header, or +environment variables, depending on the pattern—without needing to implement +custom authentication logic or manage secrets directly. + +## Backend authentication patterns + +ToolHive supports three patterns for backend authentication. Which one you use +depends on the relationship between your identity provider (IdP) and the backend +service. + +### Static credentials and API keys + +When a backend service requires API keys, database passwords, or other static +credentials, you can configure them directly in ToolHive as environment +variables, secret files, or injected headers. + +This is the simplest pattern, but it provides the least security and +auditability. Static credentials are long-lived and shared across all users, so +there is no per-user attribution in audit logs. + +When using static credentials, consider integrating with a secret management +system like [HashiCorp Vault](../integrations/vault.mdx) for secure storage and +rotation. + +### Token exchange + +When the backend service trusts the same IdP as your MCP clients—or federation +is configured between the two IdPs—ToolHive can exchange the client's token for +one scoped to the backend service using RFC 8693 token exchange. This preserves +the user's identity across services and provides short-lived, narrowly scoped +tokens. Because the trust relationship is pre-configured at the IdP, the +exchange is transparent to the end user—no consent screen required. + +#### Same IdP with token exchange + +When both the MCP server and the backend service trust the same IdP, and that +IdP supports [RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) token +exchange, ToolHive can exchange the internal token for an external one. + +```mermaid +flowchart LR + User[User] + IDP[Identity Provider] + ToolHive[ToolHive Middleware] + MCP[MCP Server] + Upstream[Upstream Service] + + User -->|login & receive token| IDP + User -->|request with token| ToolHive + ToolHive -->|subject_token → exchange| IDP + IDP -->|external_token issued| ToolHive + ToolHive -->|pass external_token| MCP + MCP -->|calls with external_token| Upstream + Upstream -->|validates token| IDP +``` + +**How it works:** + +1. The user authenticates to the MCP client and receives an access token from + the IdP +2. ToolHive's token exchange middleware contacts the IdP, presenting the user's + access token +3. The IdP issues a new access token with different audience and scopes +4. ToolHive passes this access token to the MCP server +5. The MCP server uses the access token to call the upstream service + +#### Federated IdPs with identity mapping + +When the backend service trusts a different IdP, but federation is configured +between the two IdPs, ToolHive can use the federated identity service to issue +short-lived tokens. Examples include Google's Security Token Service (STS) for +Google Cloud services and AWS STS for AWS services—both can issue tokens based +on your corporate identity. + +```mermaid +flowchart LR + User[User] + IDP1[IdP A - User Login] + ToolHive[ToolHive Middleware] + IDP2[IdP B - Federated] + MCP[MCP Server] + Upstream[Upstream Service] + + User -->|login & receive token_A| IDP1 + User -->|request with token_A| ToolHive + ToolHive -->|submit token_A| IDP2 + IDP2 -->|issue token_B| ToolHive + ToolHive -->|pass token_B| MCP + MCP -->|call with token_B| Upstream + Upstream -->|validate token_B| IDP2 +``` + +**How it works:** + +1. The user authenticates to their MCP client with a corporate IdP and receives + token_A +2. ToolHive submits token_A to the federated identity service +3. The federated service maps the identity and issues token_B +4. ToolHive passes token_B to the MCP server +5. The MCP server uses token_B to call the upstream service + +### Embedded authorization server + +When the MCP server needs to call an external API where no federation +relationship exists—such as GitHub, Google Workspace, or Atlassian APIs—the +embedded authorization server handles the full OAuth web flow against the +external provider. The proxy redirects the user to authenticate directly with +the external service, obtains tokens on behalf of the user, and passes the +upstream token to the MCP server. + +```mermaid +sequenceDiagram + participant User + participant Proxy as ToolHive Proxy + participant ExtProvider as External Provider + + User->>Proxy: Connect + Proxy-->>User: Redirect to login + User->>ExtProvider: Authenticate + ExtProvider->>Proxy: Authorization code + Proxy->>ExtProvider: Exchange code for token + ExtProvider->>Proxy: Upstream tokens + Proxy->>User: Issue JWT +``` + +On subsequent MCP requests, ToolHive uses the JWT to retrieve the stored +upstream tokens and forward them to the MCP server. For details on this +mechanism, see [Token storage and forwarding](#token-storage-and-forwarding). + +The embedded authorization server runs in-process within the ToolHive proxy—no +separate infrastructure is needed. It supports Dynamic Client Registration +(DCR), so MCP clients can register automatically with ToolHive—no manual client +configuration in ToolHive is required. + +:::note + +The embedded authorization server is currently available only for Kubernetes +deployments using the ToolHive Operator. + +::: + +#### Key characteristics + +- **In-process execution:** The authorization server runs within the ToolHive + proxy—no separate infrastructure or sidecar containers needed. +- **Configurable signing keys:** JWTs are signed with keys you provide, + supporting key rotation for zero-downtime updates. +- **Flexible upstream providers:** Supports both OIDC providers (with automatic + endpoint discovery) and OAuth 2.0 providers (with explicit endpoint + configuration). +- **Configurable token lifespans:** Access tokens, refresh tokens, and + authorization codes have configurable durations with sensible defaults. +- **Dynamic Client Registration (DCR):** Supports OAuth 2.0 Dynamic Client + Registration (RFC 7591), allowing MCP clients to register automatically with + ToolHive's authorization server—no manual client registration in ToolHive is + required. +- **Direct upstream redirect:** The embedded authorization server redirects + clients directly to the upstream provider for authentication (for example, + GitHub or Atlassian). +- **Single upstream provider:** Currently supports one upstream identity + provider per configuration. + +:::info[Chained authentication not yet supported] + +The embedded authorization server redirects clients directly to the upstream +provider. This means the upstream provider must be the service whose API the MCP +server calls. Chained authentication—where a client authenticates with a +corporate IdP like Okta, which then federates to an external provider like +GitHub—is not yet supported. If your deployment requires this pattern, consider +using [token exchange](#same-idp-with-token-exchange) with a federated identity +provider instead. + +::: + +#### Token storage and forwarding + +The embedded authorization server stores upstream tokens (access tokens, refresh +tokens, and ID tokens from external providers) in session storage. When the +OAuth flow completes, the server generates a unique session ID and stores the +upstream tokens keyed by this ID. The JWT issued to the client contains a `tsid` +(Token Session ID) claim that references this session. + +When a client makes an MCP request with this JWT: + +1. The ToolHive proxy validates the JWT signature and extracts the `tsid` claim +2. It retrieves the upstream tokens from session storage using the `tsid` +3. The proxy replaces the `Authorization` header with the upstream access token +4. The request is forwarded to the MCP server with the external provider's token + +```mermaid +sequenceDiagram + participant Client + participant Proxy as ToolHive Proxy + participant Store as Session Storage + participant MCP as MCP Server + participant API as External API + + Note over Client,Store: Initial OAuth flow + Proxy->>Store: Store upstream tokens
keyed by session ID + Proxy-->>Client: Issue JWT with tsid claim + + Note over Client,API: Subsequent MCP requests + Client->>Proxy: MCP request with JWT + Proxy->>Proxy: Validate JWT signature + Proxy->>Store: Look up upstream token
using tsid from JWT + Store-->>Proxy: Return upstream access token + Proxy->>MCP: Forward request with
upstream access token + MCP->>API: Call external API + API-->>MCP: Response + MCP-->>Proxy: Response + Proxy-->>Client: Response +``` + +This mechanism allows MCP servers to call external APIs with the user's actual +credentials from the upstream provider, while the client only needs to manage a +single ToolHive-issued JWT. + +#### Automatic token refresh + +Upstream access tokens have their own expiration, independent of the ToolHive +JWT lifespan. When the stored upstream access token has expired, ToolHive +automatically refreshes it using the stored refresh token before forwarding the +request — your MCP session continues without re-authentication. + +If the refresh token is also expired or has been revoked by the upstream +provider, ToolHive returns a `401` response, prompting you to re-authenticate +through the OAuth flow. + +:::warning[Session storage limitations] + +By default, session storage is in-memory only. Upstream tokens are lost when +pods restart, requiring users to re-authenticate. For production deployments, +configure Redis Sentinel as the storage backend for persistent, highly available +session storage. See +[Configure session storage](../guides-k8s/auth-k8s.mdx#configure-session-storage) +for a quick setup, or the full +[Redis Sentinel session storage](../guides-k8s/redis-session-storage.mdx) +tutorial for an end-to-end walkthrough. + +::: + +For the client-facing OAuth flow, see +[Embedded authorization server](./auth-framework.mdx#embedded-authorization-server). +For Kubernetes setup instructions, see +[Set up embedded authorization server authentication](../guides-k8s/auth-k8s.mdx#set-up-embedded-authorization-server-authentication). + +## Token exchange in depth + +This section provides implementation details for the token exchange patterns +described above. For setup instructions, see +[Configure token exchange](../guides-cli/token-exchange.mdx) (CLI) or +[Configure token exchange in Kubernetes](../guides-k8s/token-exchange-k8s.mdx). + +### Same IdP with token exchange + +The token exchange flow demonstrates how ToolHive transforms user identity +tokens into properly scoped service tokens. + +```mermaid +sequenceDiagram + participant Client + participant ToolHive + participant IDP + participant MCP Server + participant Upstream Service + + Client->>ToolHive: Request with user token + ToolHive->>IDP: Validate token + IDP-->>ToolHive: Token valid + ToolHive->>IDP: Exchange token request + IDP-->>ToolHive: Service token + ToolHive->>MCP Server: Request with service token + MCP Server->>Upstream Service: API call + Upstream Service-->>MCP Server: Response + MCP Server-->>ToolHive: Response + ToolHive-->>Client: Response +``` + +When a client authenticates to ToolHive, it receives a token scoped for the MCP +server: + +```json +{ + "iss": "https://idp.example.com/oauth2/default", + "aud": "mcp-server", + "scp": ["backend-mcp:tools:call", "backend-mcp:tools:list"], + "sub": "user@example.com" +} +``` + +ToolHive's token exchange middleware contacts the IdP and exchanges this token +for one scoped to the backend service: + +```json +{ + "iss": "https://idp.example.com/oauth2/default", + "aud": "backend-server", + "scp": ["backend-api:read"], + "sub": "user@example.com" +} +``` + +Notice how the audience (`aud`) and scopes (`scp`) change while preserving the +user's identity (`sub`). This exchanged token is then injected into the +`Authorization: Bearer` HTTP header and passed to the MCP server. + +### Federated IdPs with identity mapping + +When using federated identity providers, ToolHive can map your corporate +identity to an external service identity. This is particularly useful for +accessing cloud services like Google Cloud Platform. + +The client authenticates with your corporate IdP and receives a token: + +```json +{ + "iss": "https://idp.example.com/oauth2/default", + "aud": "mcp-server", + "sub": "user@example.com", + "email": "user@example.com", + "scp": ["mcp:tools:call", "mcp:tools:list"], + "exp": 1729641600, + "iat": 1729638000 +} +``` + +ToolHive's token exchange middleware calls the external Security Token Service +(STS) endpoint. For Google Cloud, this looks like: + +```http +POST https://sts.googleapis.com/v1/token +Content-Type: application/x-www-form-urlencoded + +grant_type=urn:ietf:params:oauth:grant-type:token-exchange +&audience=//iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/providers/PROVIDER_ID +&scope=https://www.googleapis.com/auth/bigquery +&requested_token_type=urn:ietf:params:oauth:token-type:access_token +&subject_token=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9... +&subject_token_type=urn:ietf:params:oauth:token-type:jwt +``` + +The federated service returns a token that maps your corporate identity to a +federated principal: + +```json +{ + "iss": "https://sts.googleapis.com", + "sub": "principal://iam.googleapis.com/projects/PROJECT_NUMBER/locations/global/workloadIdentityPools/POOL_ID/subject/user@example.com", + "aud": "https://bigquery.googleapis.com/", + "email": "user@example.com", + "scope": "https://www.googleapis.com/auth/bigquery", + "exp": 1729641600, + "iat": 1729638000 +} +``` + +This exchanged token is injected into the `Authorization: Bearer` HTTP header +and passed to the MCP server. The MCP server uses this token to make upstream +API calls, with each request attributed to the individual user's federated +identity rather than a shared service account. + +Federation-based token exchange has several important characteristics that +distinguish it from standard token exchange: + +1. **No client authentication required:** The external STS endpoint doesn't + require `client_id` or `client_secret`. The OAuth JWT itself serves as proof + of identity. +2. **Identity federation pool as intermediary:** The `audience` parameter points + to a federation pool configuration, not directly to the target service. +3. **Principal mapping:** User attributes (email, sub) from the OAuth token are + mapped to federated principals for access control. +4. **Individual audit trail:** Upstream service audit logs show the individual + user identity, not a service account. + +## Security and operational benefits + +ToolHive's token-based authentication patterns (token exchange and the embedded +authorization server) provide several key advantages over static credentials: + +- **Secure:** MCP servers receive short-lived, properly scoped access tokens + instead of embedding long-lived secrets +- **Auditable:** Each API call is attributed to the individual user identity, + making audit trails clear and meaningful +- **Multi-tenant friendly:** Token scoping naturally supports tenant isolation + and separation of duties +- **Developer friendly:** MCP servers don't need custom authentication + logic—they just use the provided token +- **Least privilege:** Tokens are narrowly scoped to specific audiences and + permissions, reducing the blast radius if compromised +- **Consistent:** The same pattern works across different backend services and + identity providers + +## Choosing the right backend authentication pattern + +| Scenario | Pattern | Why | +| ------------------------------------------------------------------- | -------------------------------------------------------------------- | ----------------------------------------------------------------- | +| Backend only accepts API keys or static credentials | [Static credentials](#static-credentials-and-api-keys) | No OAuth support; configure credentials directly in ToolHive | +| Backend trusts the same IdP as your clients | [Token exchange (same IdP)](#same-idp-with-token-exchange) | Exchange the client token for a backend-scoped token via RFC 8693 | +| Backend trusts a federated IdP (for example, Google Cloud) | [Token exchange (federation)](#federated-idps-with-identity-mapping) | Map your corporate identity to the federated service | +| Backend is an external API with no federation (for example, GitHub) | [Embedded authorization server](#embedded-authorization-server) | Run the full OAuth flow against the external provider | + +### Built-in AWS STS support + +For AWS services like the +[AWS MCP Server](https://docs.aws.amazon.com/aws-mcp/), ToolHive has built-in +support for exchanging OIDC tokens for temporary AWS credentials using +`AssumeRoleWithWebIdentity`. This handles the STS exchange and SigV4 request +signing automatically, with claim-based IAM role selection. See the +[AWS STS integration tutorial](../integrations/aws-sts.mdx) for a step-by-step +setup guide. + +## Related information + +- For client authentication concepts, see + [Authentication and authorization](./auth-framework.mdx) +- For the embedded authorization server, see + [Embedded authorization server](./auth-framework.mdx#embedded-authorization-server) +- For configuring the embedded authorization server in Kubernetes, see + [Set up embedded authorization server authentication](../guides-k8s/auth-k8s.mdx#set-up-embedded-authorization-server-authentication) +- For configuring token exchange, see + [Configure token exchange](../guides-cli/token-exchange.mdx) (CLI) or + [Configure token exchange in Kubernetes](../guides-k8s/token-exchange-k8s.mdx) +- For policy configuration, see [Cedar policies](./cedar-policies.mdx) diff --git a/versioned_docs/version-1.1/toolhive/concepts/cedar-policies.mdx b/versioned_docs/version-1.1/toolhive/concepts/cedar-policies.mdx new file mode 100644 index 00000000..5e4089b8 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/concepts/cedar-policies.mdx @@ -0,0 +1,504 @@ +--- +title: Cedar policies +description: Writing and configuring Cedar policies for MCP server authorization. +--- + +This document provides detailed guidance on writing and configuring Cedar +policies for MCP server authorization. You'll learn how to create effective +policies, configure authorization settings, and troubleshoot common issues. + +:::info + +For the conceptual overview of authentication and authorization, see +[Authentication and authorization framework](./auth-framework.mdx). + +::: + +## Cedar policy language + +Cedar policies express authorization rules in a clear, declarative syntax: + +```text +permit|forbid(principal, action, resource) when { conditions }; +``` + +- `permit` or `forbid`: Whether to allow or deny the operation +- `principal`: The entity making the request (the client) +- `action`: The operation being performed +- `resource`: The object being accessed +- `conditions`: Optional conditions that must be satisfied + +## MCP-specific entities + +In the context of MCP servers, Cedar policies use the following entities: + +### Principal + +The client making the request, identified by the `sub` claim in the access +token: + +- Format: `Client::` +- Example: `Client::user123` + +### Action + +The operation being performed on an MCP feature: + +- Format: `Action::` +- Examples: + - `Action::"call_tool"`: Call a tool + - `Action::"get_prompt"`: Get a prompt + - `Action::"read_resource"`: Read a resource + - `Action::"list_tools"`: List available tools + +### Resource + +The object being accessed: + +- Format: `::` +- Examples: + - `Tool::"weather"`: The weather tool + - `Prompt::"greeting"`: The greeting prompt + - `Resource::"data"`: The data resource + +## Configuration formats + +You can configure Cedar authorization using either JSON or YAML format: + +### JSON configuration + +```json +{ + "version": "1.0", + "type": "cedarv1", + "cedar": { + "policies": [ + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"weather\");", + "permit(principal, action == Action::\"get_prompt\", resource == Prompt::\"greeting\");", + "permit(principal, action == Action::\"read_resource\", resource == Resource::\"data\");" + ], + "entities_json": "[]" + } +} +``` + +### YAML configuration + +```yaml +version: '1.0' +type: cedarv1 +cedar: + policies: + - 'permit(principal, action == Action::"call_tool", resource == + Tool::"weather");' + - 'permit(principal, action == Action::"get_prompt", resource == + Prompt::"greeting");' + - 'permit(principal, action == Action::"read_resource", resource == + Resource::"data");' + entities_json: '[]' +``` + +### Configuration fields + +- `version`: The version of the configuration format +- `type`: The type of authorization configuration (currently only `cedarv1` is + supported) +- `cedar`: The Cedar-specific configuration + - `policies`: An array of Cedar policy strings + - `entities_json`: A JSON string representing Cedar entities + +## Writing effective policies + +Understanding how to write Cedar policies is crucial for securing your MCP +servers effectively. This section provides practical guidance for creating +policies that match your security requirements. + +### Basic policy patterns + +Start with simple policies and build complexity as needed: + +#### Allow specific tool access + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"weather"); +``` + +This policy allows any authenticated client to call the weather tool. It's +useful when you want to provide broad access to specific functionality. + +#### Allow specific user access + +```text +permit(principal == Client::"user123", action == Action::"call_tool", resource); +``` + +This policy allows a specific user to call any tool. Use this pattern when you +need to grant broad permissions to trusted users. + +### Role-based access control (RBAC) + +RBAC policies use roles from JWT claims to determine access: + +```text +permit(principal, action == Action::"call_tool", resource) when { + principal.claim_roles.contains("admin") +}; +``` + +This policy allows clients with the "admin" role to call any tool. RBAC is +effective when you have well-defined roles in your organization. + +### Attribute-based access control (ABAC) + +ABAC policies use multiple attributes to make fine-grained decisions: + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"sensitive_data") when { + principal.claim_roles.contains("data_analyst") && + resource.arg_data_level <= principal.claim_clearance_level +}; +``` + +This policy allows data analysts to access sensitive data, but only if their +clearance level is sufficient. ABAC provides the most flexibility for complex +security requirements. + +## Working with JWT claims + +JWT claims from your identity provider become available in policies with a +`claim_` prefix. You can use these claims in two ways: + +**On the principal entity:** + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"weather") when { + principal.claim_name == "John Doe" +}; +``` + +**In the context:** + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"weather") when { + context.claim_name == "John Doe" +}; +``` + +Both approaches work identically. Choose the one that makes your policies more +readable. + +## Working with tool arguments + +Tool arguments become available in policies with an `arg_` prefix. This lets you +create policies based on the specific parameters of requests: + +**On the resource entity:** + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"weather") when { + resource.arg_location == "New York" || resource.arg_location == "London" +}; +``` + +**In the context:** + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"weather") when { + context.arg_location == "New York" || context.arg_location == "London" +}; +``` + +This policy allows weather tool calls only for specific locations, demonstrating +how you can control access based on request parameters. + +## List operations and filtering + +List operations (`tools/list`, `prompts/list`, `resources/list`) work +differently from other operations. They're always allowed, but the response is +automatically filtered based on what the user can actually access: + +- `tools/list` shows only tools the user can call (based on `call_tool` + policies) +- `prompts/list` shows only prompts the user can get (based on `get_prompt` + policies) +- `resources/list` shows only resources the user can read (based on + `read_resource` policies) + +You don't need to write explicit policies for list operations. Instead, focus on +the underlying access policies, and the lists will be filtered automatically. + +For example, if you have this policy: + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"weather"); +``` + +Then `tools/list` will only show the "weather" tool for that user. + +## Optimizer meta-tool enforcement + +When the [optimizer](../guides-vmcp/optimizer.mdx) is enabled alongside Cedar +authorization, Cedar policies cover the optimizer's `find_tool` and `call_tool` +meta-tools: + +- **`tools/list`**: The meta-tools (`find_tool`, `call_tool`) pass through Cedar + filtering. Real backend tools are filtered as before. +- **`tools/call` with `call_tool`**: Cedar extracts the inner `tool_name` + argument and authorizes the actual backend tool before execution. Your + existing per-tool policies apply transparently. +- **`tools/call` with `find_tool`**: The response is filtered through Cedar so + clients cannot discover unauthorized tools via search. + +You don't need to write separate policies for the meta-tools themselves. Your +existing `call_tool` policies on backend tools are enforced automatically when +the optimizer routes calls. + +:::warning[Review policies when enabling Cedar with the optimizer] + +If you enable Cedar on a deployment that already uses the optimizer, ensure your +backend tool policies are comprehensive. Previously unchecked operations are now +subject to default-deny authorization. Tools that were accessible without +policies before may now be denied. + +::: + +## Upstream identity provider claims + +When using the +[embedded authorization server](./auth-framework.mdx#embedded-authorization-server), +Cedar policies can reference claims from the upstream identity provider token +(for example, GitHub `login` or Okta `groups`). This enables group-based +authorization using your organization's existing identity provider groups. + +### Group-based authorization + +The Cedar authorizer extracts group membership from upstream tokens using +configurable claim names. By default, it looks for `groups`, `roles`, and +`cognito:groups` claims. Groups are mapped to `THVGroup` parent entities, so you +can write policies like: + +```text +permit( + principal in THVGroup::"engineering", + action == Action::"call_tool", + resource +); +``` + +This permits any user in the "engineering" group to call any tool. + +### Custom group claim names + +If your identity provider uses a non-standard claim name for groups (for +example, Auth0 and Okta often use URI-style claims like +`https://example.com/groups`), set the `group_claim_name` option in your Cedar +configuration: + +```json +{ + "version": "1.0", + "type": "cedarv1", + "cedar": { + "policies": [ + "permit(principal in THVGroup::\"engineering\", action, resource);" + ], + "entities_json": "[]", + "group_claim_name": "https://example.com/groups" + } +} +``` + +When `group_claim_name` is set, it takes priority over the well-known defaults. +When it is empty (the default), ToolHive checks `groups`, `roles`, and +`cognito:groups` in order. + +### How it works + +1. The embedded authorization server authenticates the user with your upstream + identity provider and issues a ToolHive JWT. +2. The Cedar authorizer reads claims from the upstream token (not just the + ToolHive-issued JWT). +3. Group claims are extracted and used to build `THVGroup` parent entities for + the principal. +4. Policies using `principal in THVGroup::""` evaluate correctly. + +:::note + +If the upstream token is opaque (not a JWT), the authorizer denies the request. +There is no silent fallback to ToolHive-issued claims only. + +::: + +## Policy evaluation and secure defaults + +Understanding how Cedar evaluates policies helps you write more effective and +secure authorization rules. + +### Evaluation order + +ToolHive's policy evaluation follows a secure-by-default, least-privilege model: + +1. **Deny precedence:** If any `forbid` policy matches, the request is denied +2. **Permit evaluation:** If any `permit` policy matches, the request is + authorized +3. **Default deny:** If no policy matches, the request is denied + +This means that `forbid` policies always override `permit` policies, and any +request not explicitly permitted is denied. This approach minimizes risk and +ensures that only authorized actions are allowed. + +### Designing secure policies + +When writing policies, follow these principles: + +**Start with least privilege:** Begin by denying everything, then add specific +permissions as needed. This approach is more secure than starting with broad +permissions and then trying to restrict them. + +**Use explicit deny sparingly:** While `forbid` policies can be useful, they can +also make your policy set harder to understand. In most cases, the default deny +behavior is sufficient. + +**Test your policies:** Always test policies with real requests to ensure they +work as expected. Pay special attention to edge cases and error conditions. + +## Advanced policy examples + +### Combining JWT claims and tool arguments + +You can combine JWT claims and tool arguments in your policies to create more +sophisticated authorization rules: + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"sensitive_data") when { + principal.claim_roles.contains("data_analyst") && + resource.arg_data_level <= principal.claim_clearance_level +}; +``` + +This policy allows clients with the "data_analyst" role to access the +sensitive_data tool, but only if their clearance level (from JWT claims) is +sufficient for the requested data level (from tool arguments). + +### Multi-tenant environments + +In multi-tenant environments, you can use policies to isolate tenants: + +```text +permit(principal, action, resource) when { + principal.claim_tenant_id == resource.tenant_id +}; +``` + +This ensures that clients can only access resources belonging to their tenant. + +### Data sensitivity levels + +For data with different sensitivity levels: + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"data_access") when { + principal.claim_clearance_level >= resource.arg_data_sensitivity +}; +``` + +This ensures that clients can only access data within their clearance level. + +### Geographic restrictions + +For geographically restricted resources: + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"geo_restricted") when { + principal.claim_location in ["US", "Canada", "Mexico"] +}; +``` + +This restricts access based on the client's location. + +### Time-based access + +For resources that should only be accessible during certain hours: + +```text +permit(principal, action == Action::"call_tool", resource == Tool::"business_hours") when { + context.current_hour >= 9 && context.current_hour <= 17 +}; +``` + +This restricts access to business hours only. + +## Entity attributes + +Cedar entities can have attributes that can be used in policy conditions. The +authorization middleware automatically adds JWT claims and tool arguments as +attributes to the principal entity. + +You can also define custom entities with attributes in the `entities_json` field +of the configuration file: + +```json +{ + "version": "1.0", + "type": "cedarv1", + "cedar": { + "policies": [ + "permit(principal, action == Action::\"call_tool\", resource) when { resource.owner == principal.claim_sub };" + ], + "entities_json": "[ + { + \"uid\": \"Tool::weather\", + \"attrs\": { + \"owner\": \"user123\" + } + } + ]" + } +} +``` + +This configuration defines a custom entity for the weather tool with an `owner` +attribute set to `user123`. The policy allows clients to call tools only if they +own them. + +## Troubleshooting policies + +When policies don't work as expected, follow this systematic approach: + +### Request is denied unexpectedly + +1. **Check policy syntax:** Ensure your policies are correctly formatted and use + valid Cedar syntax. +2. **Verify entity matching:** Confirm that the principal, action, and resource + in your policies match the actual values in the request. +3. **Test conditions:** Check that any conditions in your policies are satisfied + by the request context. +4. **Remember default deny:** If no policy explicitly permits the request, it + will be denied. + +### JWT claims are not available + +1. **Verify JWT middleware:** Ensure that JWT authentication is configured + correctly and running before authorization. +2. **Check token claims:** Verify that the JWT token contains the expected + claims. +3. **Use correct prefix:** Remember that JWT claims are available with a + `claim_` prefix. + +### Tool arguments are not available + +1. **Check request format:** Ensure that tool arguments are correctly specified + in the request. +2. **Use correct prefix:** Remember that tool arguments are available with an + `arg_` prefix. +3. **Verify argument names:** Confirm that the argument names in your policies + match those in the actual requests. + +## Related information + +- For the conceptual overview, see + [Authentication and authorization framework](./auth-framework.mdx) +- For detailed Cedar policy syntax, see + [Cedar documentation](https://docs.cedarpolicy.com/) +- For a practical example of Cedar policies enforcing role-based access control, + see [Role-based authorization with Okta](../integrations/okta.mdx) diff --git a/versioned_docs/version-1.1/toolhive/concepts/embedded-auth-server.mdx b/versioned_docs/version-1.1/toolhive/concepts/embedded-auth-server.mdx new file mode 100644 index 00000000..109bb07d --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/concepts/embedded-auth-server.mdx @@ -0,0 +1,193 @@ +--- +title: Embedded authorization server +description: + How the ToolHive embedded authorization server works, including the OAuth + flow, token storage and forwarding, and when to use it. +--- + +The embedded authorization server is an OAuth 2.0 authorization server that runs +in-process within the ToolHive proxy. It solves a specific problem: how to +authenticate MCP server requests to external APIs—like GitHub, Google Workspace, +or Atlassian—where no federation relationship exists between your identity +provider and that external service. + +Without the embedded auth server, every MCP client would need to register its +own OAuth application with each external provider, manage redirect URIs, and +handle token acquisition separately. The embedded auth server centralizes this: +it handles the full OAuth web flow against the external provider on behalf of +clients, stores the resulting tokens, and issues its own JWTs that clients use +for subsequent requests. + +:::note + +The embedded authorization server is currently available only for Kubernetes +deployments using the ToolHive Operator. + +::: + +## When to use the embedded authorization server + +Use the embedded authorization server when your MCP servers call external APIs +on behalf of individual users and no federation relationship exists between your +identity provider and those services. + +| Scenario | Pattern to use | +| ------------------------------------------------------------------- | -------------------------------------------------------------------------------------- | +| Backend only accepts API keys or static credentials | [Static credentials](./backend-auth.mdx#static-credentials-and-api-keys) | +| Backend trusts the same IdP as your clients | [Token exchange (same IdP)](./backend-auth.mdx#same-idp-with-token-exchange) | +| Backend trusts a federated IdP (for example, Google Cloud, AWS) | [Token exchange (federation)](./backend-auth.mdx#federated-idps-with-identity-mapping) | +| Backend is an external API with no federation (for example, GitHub) | **Embedded authorization server** (this page) | + +## How the OAuth flow works + +From the client's perspective, the embedded authorization server provides a +standard OAuth 2.0 experience: + +1. If the client is not yet registered, it registers via Dynamic Client + Registration (DCR, RFC 7591), receiving a `client_id` and `client_secret`. No + manual client registration in ToolHive is required. +2. The client is directed to the ToolHive authorization endpoint. +3. ToolHive redirects the client to the upstream identity provider for + authentication (for example, signing in with GitHub or Atlassian). +4. ToolHive exchanges the authorization code for upstream tokens and issues its + own JWT to the client, signed with keys you configure. +5. The client includes this JWT as a `Bearer` token in the `Authorization` + header on subsequent requests. + +```mermaid +sequenceDiagram + participant User + participant Proxy as ToolHive Proxy + participant ExtProvider as External Provider + + User->>Proxy: Connect + Proxy-->>User: Redirect to login + User->>ExtProvider: Authenticate + ExtProvider->>Proxy: Authorization code + Proxy->>ExtProvider: Exchange code for token + ExtProvider->>Proxy: Upstream tokens + Proxy->>User: Issue JWT +``` + +Behind the scenes, ToolHive stores the upstream tokens in session storage and +uses them to authenticate MCP server requests to external APIs. The client only +manages a single ToolHive-issued JWT. + +## Token storage and forwarding + +When the OAuth flow completes, the embedded auth server generates a unique +session ID and stores the upstream tokens (access token, refresh token, and ID +token from the external provider) keyed by this ID in session storage. The JWT +issued to the client contains a `tsid` (Token Session ID) claim that references +this session. + +When a client makes an MCP request with this JWT: + +1. The ToolHive proxy validates the JWT signature and extracts the `tsid` claim. +2. It retrieves the upstream tokens from session storage using the `tsid`. +3. The proxy replaces the `Authorization` header with the upstream access token. +4. The request is forwarded to the MCP server with the external provider's + token. + +```mermaid +sequenceDiagram + participant Client + participant Proxy as ToolHive Proxy + participant Store as Session Storage + participant MCP as MCP Server + participant API as External API + + Note over Client,Store: Initial OAuth flow + Proxy->>Store: Store upstream tokens
keyed by session ID + Proxy-->>Client: Issue JWT with tsid claim + + Note over Client,API: Subsequent MCP requests + Client->>Proxy: MCP request with JWT + Proxy->>Proxy: Validate JWT signature + Proxy->>Store: Look up upstream token
using tsid from JWT + Store-->>Proxy: Return upstream access token + Proxy->>MCP: Forward request with
upstream access token + MCP->>API: Call external API + API-->>MCP: Response + MCP-->>Proxy: Response + Proxy-->>Client: Response +``` + +MCP servers receive the upstream access token in the `Authorization: Bearer` +header—they don't need to implement custom authentication logic or manage +secrets. + +## Automatic token refresh + +Upstream access tokens expire independently of the ToolHive JWT lifespan. When +the stored upstream access token has expired, ToolHive automatically refreshes +it using the stored refresh token before forwarding the request. Your MCP +session continues without re-authentication. + +If the refresh token is also expired or has been revoked by the upstream +provider, ToolHive returns a `401` response, prompting re-authentication through +the OAuth flow. + +## Key characteristics + +- **In-process execution:** The authorization server runs within the ToolHive + proxy—no separate infrastructure or sidecar containers needed. +- **Dynamic Client Registration (DCR):** Supports OAuth 2.0 DCR (RFC 7591), + allowing MCP clients to register automatically. No manual client registration + in ToolHive is required. +- **Direct upstream redirect:** Redirects clients directly to the upstream + provider for authentication (for example, GitHub or Atlassian). +- **Configurable signing keys:** JWTs are signed with keys you provide, + supporting key rotation for zero-downtime updates. +- **Flexible upstream providers:** Supports OIDC providers (with automatic + endpoint discovery) and plain OAuth 2.0 providers (with explicit endpoint + configuration). +- **Configurable token lifespans:** Access tokens, refresh tokens, and + authorization codes have configurable durations with sensible defaults. + +## Session storage + +By default, session storage is in-memory. Upstream tokens are lost when pods +restart, requiring users to re-authenticate. + +For production deployments, configure Redis Sentinel as the storage backend for +persistent, highly available session storage. See +[Configure session storage](../guides-k8s/auth-k8s.mdx#configure-session-storage) +for a quick setup, or the full +[Redis Sentinel session storage](../guides-k8s/redis-session-storage.mdx) guide +for an end-to-end walkthrough. + +## MCPServer vs. VirtualMCPServer + +The embedded auth server is available on both `MCPServer` and `VirtualMCPServer` +resources, with some differences: + +| | MCPServer | VirtualMCPServer | +| ---------------------- | ------------------------------------------- | ------------------------------------------------------------------------------ | +| Configuration location | Separate `MCPExternalAuthConfig` resource | Inline `authServerConfig` block on the resource | +| Upstream providers | Single upstream provider | Multiple upstream providers with sequential authorization chaining | +| Token forwarding | Automatic (single provider, single backend) | Explicit `upstreamInject` or `tokenExchange` config maps providers to backends | + +For single-backend deployments on MCPServer, the embedded auth server +automatically swaps the token for each request. For vMCP with multiple backends, +you configure which upstream provider's token goes to which backend using +[upstream token injection](../guides-vmcp/authentication.mdx#upstream-token-injection) +or +[token exchange with upstream tokens](../guides-vmcp/authentication.mdx#token-exchange-with-upstream-tokens). + +## Next steps + +- [Set up embedded authorization server authentication](../guides-k8s/auth-k8s.mdx#set-up-embedded-authorization-server-authentication) + — step-by-step setup for MCPServer resources in Kubernetes +- [vMCP embedded authorization server](../guides-vmcp/authentication.mdx#embedded-authorization-server) + — configuring multiple upstream providers on a VirtualMCPServer +- [Redis Sentinel session storage](../guides-k8s/redis-session-storage.mdx) — + production session storage configuration + +## Related information + +- [Authentication and authorization](./auth-framework.mdx) — client-to-MCP + authentication concepts and the overall framework +- [Backend authentication](./backend-auth.mdx) — all backend authentication + patterns, including when to choose the embedded auth server +- [Cedar policies](./cedar-policies.mdx) — authorization policy configuration diff --git a/versioned_docs/version-1.1/toolhive/concepts/groups.mdx b/versioned_docs/version-1.1/toolhive/concepts/groups.mdx new file mode 100644 index 00000000..1d10e784 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/concepts/groups.mdx @@ -0,0 +1,161 @@ +--- +title: Organizing MCP servers with groups +sidebar_label: Organizing MCP servers +description: + Understanding when and why to use groups for organizing MCP servers and + controlling client access. +--- + +As your MCP server usage grows, managing multiple servers becomes increasingly +complex. Groups provide a way to organize servers logically and control which +tools are available to different clients. + +## The problem groups solve + +Without groups, all your MCP servers live in a single pool. This creates +challenges: + +- **Tool overload**: AI clients see every tool from every server, making it + harder to select the right one +- **No separation of concerns**: Development tools mix with production tools +- **One-size-fits-all access**: Every client gets access to everything, even + when they only need a subset + +Groups address these issues by letting you organize servers into logical +collections and control which clients can access each collection. + +## When to use groups + +Groups are most valuable when you need to: + +- **Separate environments**: Keep development, staging, and production servers + isolated so you don't accidentally use production tools during development +- **Organize by project**: Give each team access to the tools they need without + cluttering their workspace with irrelevant servers +- **Customize client access**: Configure different AI clients with different + tool sets based on their purpose +- **Reduce context for AI**: Limit the tools available to an AI client so it can + make better decisions about which tool to use + +### When groups aren't necessary + +Groups add organizational overhead, so they're not always the right choice: + +- **Single server setups**: If you're running just one or two MCP servers, + groups don't provide much benefit +- **Universal access needs**: If all clients need access to all servers, a + single default group works fine +- **Simple personal use**: For individual developers with straightforward needs, + the default group is often sufficient + +## Organizational patterns + +Here are common ways teams organize their groups: + +### Environment-based groups + +Separate servers by deployment environment to prevent mixing development and +production contexts: + +| Group | Purpose | +| ----------- | ---------------------------------------------- | +| development | Experimental tools, local databases, test APIs | +| staging | Pre-production testing with realistic data | +| production | Live systems with appropriate access controls | + +This pattern is useful when: + +- You run the same MCP server with different configurations per environment +- You want to prevent accidental production access during development +- Different team members need different environment access + +This pattern works well when: + +- Teams have distinct tool requirements +- You want to reduce clutter in each team's AI client +- Different teams work on different parts of your system + +### Project-based groups + +Create groups for specific projects or products: + +| Group | Servers | +| -------------- | ------------------------------------------- | +| mobile-app | Firebase, app store tools, mobile analytics | +| web-platform | CMS tools, CDN management, web analytics | +| internal-tools | Admin dashboards, reporting tools | + +This approach helps when: + +- Projects have unique tool requirements +- You want focused AI assistance for specific work contexts +- Multiple teams contribute to the same project + +## Groups and client access + +One of the most powerful aspects of groups is controlling which AI clients can +access which servers. When you configure a client for a specific group, that +client only sees servers in that group. + +This matters because: + +- **Better AI performance**: Fewer tools means the AI can make more confident + tool selections +- **Appropriate access**: Different clients can have different capabilities + based on their purpose +- **Reduced noise**: Developers see only the tools relevant to their current + work + +For example, you might configure: + +- **Visual Studio Code** with access to your development group for day-to-day + coding +- **Claude Desktop** with access to your production group for operational tasks +- **GitHub Copilot** with access to a restricted group for code review + +:::tip + +A single client can access multiple groups. This is useful when you need tools +from several areas, like a developer who works across frontend and backend. + +::: + +## Default group behavior + +Every ToolHive installation has a `default` group that cannot be deleted. This +group serves as: + +- **The starting point**: New servers go here unless you specify otherwise +- **The fallback**: Servers move here when their group is deleted +- **The simple path**: If you don't need organization, just use the default + +You don't need to create custom groups to use ToolHive effectively. The default +group works well for simple setups and individual use. + +## Designing your group structure + +When planning your groups, consider: + +1. **Start simple**: Begin with the default group and add custom groups only + when you feel the need for organization +2. **Match your workflow**: Groups should reflect how you actually work, not an + idealized structure +3. **Consider client configuration**: Think about which clients need access to + which servers +4. **Plan for growth**: Choose a pattern that scales as you add more servers + +:::info + +Each server belongs to exactly one group. If you need the same MCP server in +multiple groups (for example, a GitHub server in both development and +production), run separate instances with different names and configurations. + +::: + +## Next steps + +Now that you understand when and why to use groups, you can: + +- [Organize servers into groups (CLI)](../guides-cli/group-management.mdx) +- [Organize servers into groups (UI)](../guides-ui/group-management.mdx) +- [Configure client access](../guides-cli/client-configuration.mdx) diff --git a/versioned_docs/version-1.1/toolhive/concepts/mcp-primer.mdx b/versioned_docs/version-1.1/toolhive/concepts/mcp-primer.mdx new file mode 100644 index 00000000..d3d98330 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/concepts/mcp-primer.mdx @@ -0,0 +1,72 @@ +--- +title: 'Model Context Protocol (MCP): A friendly primer for builders' +sidebar_label: MCP primer +description: + A brief introduction to the Model Context Protocol (MCP) and its benefits for + developers. +--- + +**TL;DR:** MCP offers a pragmatic, language-friendly bridge between +probabilistic code generators and the real-world systems where your source of +truth lives. It's young, but it already solves pain points around context size, +adapter sprawl, and brittle prompts—thanks largely to an open, welcoming +developer community. If you're building next-gen coding tools, now's the ideal +moment to give MCP a spin and leave your fingerprints on the spec. + +## Why we needed something new + +Modern code-generation models work by guessing the next token from probability +space. By nature they are powerful but probabilistic and work with natural +language. Context drives everything and they can only work on what they can see. +Most real-world context developers use lives outside the model: in GitHub repos, +API docs, RFCs, and issue trackers. Bridging that gap has been messy: + +| Traditional approach | Pain point for GenAI tools | +| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------ | +| Custom adapters / plugins per data source | Hard to keep in sync; brittle when schemas change | +| Prompt stuffing (copy-pasting docs into the prompt) | Dilutes effectiveness and reduces response acceptance rates, bloats token budget, hurts latency & cost | +| REST APIs with rigid schemas | Fine for deterministic code, awkward for probabilistic LLMs that prefer natural language | + +MCP tackles these headaches by letting a model **ask external systems for facts +or files using a concise, natural syntax that itself is easy for generative +models to emit and parse.** + +## What problems does MCP solve? + +- **Token-efficient context retrieval** \ + _One-shot, structured queries_ (e.g., + `mcp://github?repo=owner/project\&path=README.md`) let the model fetch exactly + what it needs—no boilerplate, no giant system prompts. +- **Natural-language-friendly envelope** \ + The URI-like syntax is short, deterministic, yet readable enough that an LLM + can generate it without dedicated training. Embeddings created before MCP work + just fine with MCP. +- **Uniform surface over heterogeneous data** \ + Git blobs, Swagger files, Confluence pages, or a private vector store all look + like "resources" under the same scheme. Tool builders write one resolver and + get many back-ends without additional work. +- **Graceful failure semantics** \ + Every MCP response carries both _content_ and a lightweight _provenance_ + object (source, timestamp, hash). Models can decide to retry, ignore, or cite. + +## The emergence of open community + +A community has sprung up around the MCP protocol incredibly quickly. + +The spec is Apache-licensed and refreshingly small, clean, and simple, which +makes the whole thing pretty easy to grok. SDK's abound and thousands of +examples exist. The efforts of communities like golang with the go-mcp release +in April 2025 are moving server development beyond the boundaries of the +traditional JavaScript and Python ecosystems. The Golang portfolio servers +inventory is growing incredibly quickly and with it comes a wealth of production +oriented access to resources. + +There's no governing foundation yet, but a lightweight steering group triages +PRs and publishes version tags. + +## Where MCP is headed + +Expect iterative, community-driven releases—v1.0 is slated for late 2025 with a +stable core and optional capability sets (search, write-back, streaming). The +protocol's youth means rough edges, but that also means **you can still shape +it**: file issues, prototype adapters, or just lurk and learn. diff --git a/versioned_docs/version-1.1/toolhive/concepts/observability.mdx b/versioned_docs/version-1.1/toolhive/concepts/observability.mdx new file mode 100644 index 00000000..f3cb8b26 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/concepts/observability.mdx @@ -0,0 +1,497 @@ +--- +title: Observability +description: Understanding ToolHive's observability features for MCP server monitoring +--- + +ToolHive provides comprehensive observability for your MCP server interactions +through built-in OpenTelemetry instrumentation. You get complete visibility into +how your MCP servers perform, including detailed traces, metrics, and error +tracking. + +## How telemetry works + +ToolHive automatically instruments your MCP server interactions without +requiring changes to your servers. When you enable telemetry, ToolHive captures +detailed information about every request, tool call, and server interaction. + +ToolHive's telemetry captures rich, protocol-aware information because it +understands MCP operations. You get detailed traces showing tool calls, resource +access, and prompt operations rather than generic HTTP requests. + +## Distributed tracing + +Distributed tracing shows you the complete journey of each request through your +MCP servers. ToolHive creates comprehensive traces that provide end-to-end +visibility across the proxy-container boundary. + +### Trace structure + +Here's what a trace looks like when a client calls a tool in the GitHub MCP +server (some fields omitted for brevity): + +```text +Span: tools/call create_issue (150ms) +├── service.name: thv-github +├── service.version: v0.1.9 +├── http.request.method: POST +├── http.request.body.size: 256 +├── http.response.status_code: 202 +├── http.response.body.size: 1024 +├── url.full: /messages?session_id=b1d22d07-b35f-4260-9c0c-b872f92f64b1 +├── url.path: /messages +├── url.scheme: https +├── server.address: localhost:14972 +├── user_agent.original: claude-code/1.0.53 +├── mcp.method.name: tools/call +├── mcp.server.name: github +├── mcp.session.id: abc123 +├── rpc.system.name: jsonrpc +├── jsonrpc.protocol.version: 2.0 +├── jsonrpc.request.id: 5 +├── gen_ai.tool.name: create_issue +├── gen_ai.operation.name: execute_tool +├── gen_ai.tool.call.arguments: owner=stacklok, repo=toolhive, pullNumber=1131 +├── network.transport: tcp +└── network.protocol.name: http +``` + +### MCP-specific traces + +ToolHive automatically captures traces for all MCP operations, including: + +- **Tool calls** (`tools/call`) - When AI assistants use tools +- **Resource access** (`resources/read`) - When servers read files or data +- **Prompt operations** (`prompts/get`) - When servers retrieve prompts +- **Connection events** (`initialize`) - When clients connect to servers + +### Trace attributes + +Each trace includes detailed context across several layers: + +#### Service information + +```text +service.name: thv-github +service.version: v0.1.9 +host.name: my-machine +``` + +#### HTTP layer + +```text +http.request.method: POST +http.request.body.size: 256 +http.response.status_code: 202 +http.response.body.size: 1024 +url.full: /messages?session_id=b1d22d07-b35f-4260-9c0c-b872f92f64b1 +url.path: /messages +url.scheme: https +url.query: session_id=b1d22d07-b35f-4260-9c0c-b872f92f64b1 +server.address: localhost:14972 +user_agent.original: claude-code/1.0.53 +``` + +#### Network layer + +```text +network.transport: tcp +network.protocol.name: http +network.protocol.version: 1.1 +client.address: 127.0.0.1 +client.port: 52431 +``` + +#### MCP protocol details + +Details about the MCP operation being performed (some fields are specific to +each operation): + +```text +mcp.method.name: tools/call +mcp.server.name: github +mcp.session.id: abc123 +mcp.protocol.version: 2025-03-26 +rpc.system.name: jsonrpc +jsonrpc.protocol.version: 2.0 +jsonrpc.request.id: 123 +``` + +#### Method-specific attributes + +- **`tools/call`** traces include: + - `gen_ai.tool.name` - The name of the tool being called + - `gen_ai.operation.name` - Set to `execute_tool` + - `gen_ai.tool.call.arguments` - Sanitized tool arguments (sensitive values + redacted) + +- **`resources/read`** traces include: + - `mcp.resource.uri` - The URI of the resource being accessed + +- **`prompts/get`** traces include: + - `gen_ai.prompt.name` - The name of the prompt being retrieved + +- **`initialize`** traces include: + - `mcp.client.name` - The name of the connecting client (always emitted) + - `mcp.protocol.version` - The MCP protocol version negotiated + +:::note[Legacy attribute names] + +By default, ToolHive emits both the new OpenTelemetry semantic convention +attribute names shown above and legacy attribute names (e.g., `http.method`, +`mcp.method`, `mcp.tool.name`) for backward compatibility with existing +dashboards. You can control this with the `--otel-use-legacy-attributes` flag. + +::: + +## Metrics collection + +ToolHive automatically collects metrics about your MCP server usage and +performance. These metrics help you understand usage patterns, performance +characteristics, and identify potential issues. + +### Metric labels + +All metrics include consistent labels for filtering and aggregation: + +- `server` - MCP server name (e.g., `fetch`, `github`) +- `transport` - Backend transport type (`stdio`, `sse`, or `streamable-http`) +- `method` - HTTP method (`POST`, `GET`) +- `mcp_method` - MCP protocol method (e.g., `tools/call`, `resources/read`) +- `status` - Request outcome (`success` or `error`) +- `status_code` - HTTP status code (`200`, `400`, `500`) +- `tool` - Tool name for tool-specific metrics + +### Key metrics + +Example metrics from the Prometheus `/metrics` endpoint are shown below (some +fields are omitted for brevity): + +#### Request metrics + +```promql +# HELP toolhive_mcp_requests_total Total number of MCP requests +# TYPE toolhive_mcp_requests_total counter +toolhive_mcp_requests_total{mcp_method="tools/list",method="POST",server="github",status="success",status_code="202",transport="stdio"} 2 + +# HELP toolhive_mcp_request_duration_seconds Duration of MCP requests in seconds +# TYPE toolhive_mcp_request_duration_seconds histogram +toolhive_mcp_request_duration_seconds_bucket{mcp_method="tools/list",method="POST",server="github",status="success",status_code="202",transport="stdio",le="10000"} 2 +toolhive_mcp_request_duration_seconds_bucket{mcp_method="tools/list",method="POST",server="github",status="success",status_code="202",transport="stdio",le="+Inf"} 2 +toolhive_mcp_request_duration_seconds_sum{mcp_method="tools/list",method="POST",server="github",status="success",status_code="202",transport="stdio"} 0.000219416 +toolhive_mcp_request_duration_seconds_count{mcp_method="tools/list",method="POST",server="github",status="success",status_code="202",transport="stdio"} 2 +``` + +#### Connection metrics + +```promql +# HELP toolhive_mcp_active_connections Number of active MCP connections +# TYPE toolhive_mcp_active_connections gauge +toolhive_mcp_active_connections{connection_type="sse",server="github",transport="stdio"} 3 +``` + +#### Tool-specific metrics + +```promql +# HELP toolhive_mcp_tool_calls_total Total number of MCP tool calls +# TYPE toolhive_mcp_tool_calls_total counter +toolhive_mcp_tool_calls_total{server="github",status="success",tool="get_file_contents"} 15 +toolhive_mcp_tool_calls_total{server="github",status="success",tool="list_pull_requests"} 4 +toolhive_mcp_tool_calls_total{server="github",status="success",tool="search_issues"} 2 +``` + +### MCP semantic convention metrics + +In addition to the ToolHive-prefixed metrics above, ToolHive emits metrics that +follow the +[OpenTelemetry MCP semantic conventions](https://github.com/open-telemetry/semantic-conventions): + +| Metric | Type | Description | +| ------------------------------- | --------- | ---------------------------------------- | +| `mcp.server.operation.duration` | Histogram | Duration of MCP server operations | +| `mcp.client.operation.duration` | Histogram | Duration of MCP client operations (vMCP) | + +These metric names follow the OpenTelemetry MCP semantic conventions in OTLP +exports and use the same labels as the ToolHive-prefixed metrics. When exposed +via the Prometheus `/metrics` endpoint, their names are converted to +Prometheus-safe form by replacing dots (`.`) with underscores (`_`): + +- `mcp.server.operation.duration` → `mcp_server_operation_duration` +- `mcp.client.operation.duration` → `mcp_client_operation_duration` + +### vMCP metrics + +When using Virtual MCP Server (vMCP), additional metrics are available for +monitoring backend operations, workflow executions, and optimizer performance. +For details, see the +[vMCP telemetry guide](../guides-vmcp/telemetry-and-metrics.mdx). + +## Trace context propagation + +ToolHive supports two methods of trace context propagation: + +- **HTTP headers**: Standard W3C Trace Context (`traceparent` and `tracestate` + headers) and W3C Baggage propagation +- **MCP `_meta` field**: Trace context embedded in MCP request parameters via + the `params._meta` field, following the MCP specification + +When both are present, the MCP `_meta` trace context takes priority. This +enables proper trace correlation across MCP server boundaries, even when MCP +clients inject trace context into the request payload rather than HTTP headers. + +## Export options + +ToolHive supports multiple export formats to integrate with your existing +observability infrastructure. + +### OTLP export + +ToolHive supports OpenTelemetry Protocol (OTLP) export for both traces and +metrics to any compatible backend, either directly or via a collector +application. + +The [OpenTelemetry ecosystem](https://opentelemetry.io/ecosystem/vendors/) +includes a wide range of observability backends including open source solutions +like Jaeger, self-hosted solutions like Splunk, and SaaS solutions like Datadog, +New Relic, and Honeycomb. + +### Prometheus export + +ToolHive can expose Prometheus-style metrics at a `/metrics` endpoint, enabling: + +- **Direct scraping** by Prometheus servers +- **Service discovery** in Kubernetes environments +- **Integration** with existing Prometheus-based monitoring stacks + +### Dual export + +Both OTLP and Prometheus can be enabled simultaneously, allowing you to: + +- Send traces to specialized tracing backends +- Expose metrics for Prometheus scraping +- Maintain compatibility with existing monitoring infrastructure + +## Data sanitization + +ToolHive automatically protects sensitive information in traces: + +- **Sensitive arguments**: Tool arguments containing passwords, tokens, or keys + are redacted +- **Sensitive key detection**: Arguments with keys containing patterns like + "password", "token", "secret", "key", "auth", or "credential" are redacted +- **Argument truncation**: Long arguments are truncated to prevent excessive + trace size + +For example, a tool call with sensitive arguments: + +```text +gen_ai.tool.call.arguments: password=secret123, api_key=abc456, title=Bug report +``` + +ToolHive sanitizes this in the trace as: + +```text +gen_ai.tool.call.arguments: password=[REDACTED], api_key=[REDACTED], title=Bug report +``` + +## Monitoring examples + +These examples show how ToolHive's observability works in practice. + +### Tool call monitoring + +When a client calls the `create_issue` tool: + +**Request**: + +```json +{ + "jsonrpc": "2.0", + "id": "req_456", + "method": "tools/call", + "params": { + "name": "create_issue", + "arguments": { + "title": "Bug report", + "body": "Found an issue with the API" + } + } +} +``` + +**Generated trace**: + +```text +Span: tools/call create_issue +├── mcp.method.name: tools/call +├── jsonrpc.request.id: req_456 +├── gen_ai.tool.name: create_issue +├── gen_ai.tool.call.arguments: title=Bug report, body=Found an issue with... +├── mcp.server.name: github +├── network.transport: tcp +├── http.request.method: POST +├── http.response.status_code: 200 +└── duration: 850ms +``` + +**Generated metrics**: + +```promql +toolhive_mcp_requests_total{mcp_method="tools/call",server="github",status="success"} 1 +toolhive_mcp_request_duration_seconds{mcp_method="tools/call",server="github"} 0.85 +toolhive_mcp_tool_calls_total{server="github",tool="create_issue",status="success"} 1 +``` + +### Error tracking + +Failed requests generate error traces and metrics: + +**Error trace**: + +```text +Span: tools/call invalid_tool +├── mcp.method.name: tools/call +├── gen_ai.tool.name: invalid_tool +├── http.response.status_code: 400 +├── span.status: ERROR +├── span.status_message: Tool not found +└── duration: 12ms +``` + +**Error metrics**: + +```promql +toolhive_mcp_requests_total{mcp_method="tools/call",server="github",status="error",status_code="400"} 1 +toolhive_mcp_tool_calls_total{server="github",tool="invalid_tool",status="error"} 1 +``` + +## Key performance indicators + +Monitor these key metrics for optimal MCP server performance: + +1. **Request rate**: `rate(toolhive_mcp_requests_total[5m])` +2. **Error rate**: `rate(toolhive_mcp_requests_total{status="error"}[5m])` +3. **Response time**: + `histogram_quantile(0.95, toolhive_mcp_request_duration_seconds_bucket)` +4. **Active connections**: `toolhive_mcp_active_connections` + +## Setting up dashboards and alerts + +This section shows practical examples of integrating ToolHive's observability +data with common monitoring tools. + +### Prometheus integration + +Configure Prometheus to scrape ToolHive metrics: + +```yaml title="prometheus.yml" +scrape_configs: + - job_name: 'toolhive-mcp-proxy' + static_configs: + - targets: [ + 'localhost:43832', # Example MCP server + 'localhost:51712' # Example MCP server + ] + scrape_interval: 15s + metrics_path: /metrics +``` + +### Grafana dashboard queries + +Example queries for monitoring dashboards: + +```promql +# Request rate by server +sum(rate(toolhive_mcp_requests_total[5m])) by (server) + +# Error rate percentage +sum(rate(toolhive_mcp_requests_total{status="error"}[5m])) by (server) / +sum(rate(toolhive_mcp_requests_total[5m])) by (server) * 100 + +# Response time percentiles +histogram_quantile(0.95, sum(rate(toolhive_mcp_request_duration_seconds_bucket[5m])) by (le, server)) + +# Tool usage distribution +sum(rate(toolhive_mcp_tool_calls_total[5m])) by (tool, server) + +# Active connections +toolhive_mcp_active_connections +``` + +### Alerting rules + +Example Prometheus alerting rules: + +```yaml title="prometheus.yml" +groups: + - name: toolhive-mcp-proxy + rules: + - alert: HighErrorRate + expr: rate(toolhive_mcp_requests_total{status="error"}[5m]) > 0.1 + for: 2m + labels: + severity: warning + annotations: + summary: 'High error rate in MCP proxy' + description: 'Error rate is {{ $value }} errors per second' + + - alert: HighResponseTime + expr: + histogram_quantile(0.95, toolhive_mcp_request_duration_seconds_bucket) + > 2.0 + for: 5m + labels: + severity: warning + annotations: + summary: 'High response time in MCP proxy' + description: '95th percentile response time is {{ $value }}s' + + - alert: ProxyDown + expr: up{job="toolhive-mcp-proxy"} == 0 + for: 1m + labels: + severity: critical + annotations: + summary: 'MCP proxy is down' + description: 'ToolHive MCP proxy has been down for more than 1 minute' +``` + +## Recommendations + +### Production deployment + +1. Use appropriate sampling rates (1-10% for high-traffic systems) +2. Configure authentication for OTLP endpoints +3. Use HTTPS transport in production +4. Monitor telemetry overhead with metrics +5. Set up alerting on key performance indicators + +### Development and testing + +1. Use 100% sampling for complete visibility +2. Enable local backends (Jaeger, Prometheus) +3. Test with realistic workloads to validate metrics +4. Verify trace correlation across service boundaries + +### Cost optimization + +1. Tune sampling rates based on traffic patterns +2. Use head-based sampling for consistent trace collection +3. Monitor backend costs and adjust accordingly +4. Filter out health check requests if not needed + +## Next steps + +Now that you understand how ToolHive's observability works, you can: + +1. **Choose a monitoring backend** that fits your needs and budget +2. **Follow the tutorial** to set up a local observability stack with + [OpenTelemetry, Jaeger, Prometheus, and Grafana](../integrations/opentelemetry.mdx) +3. **Enable telemetry** when running your servers: + - [using the ToolHive CLI](../guides-cli/telemetry-and-metrics.mdx) + - [using the Kubernetes operator](../guides-k8s/telemetry-and-metrics.mdx) +4. **Set up basic dashboards** to track request rates, error rates, and response + times +5. **Configure alerts** for critical issues + +The telemetry system works automatically once enabled, providing immediate +insights into your MCP server performance and usage patterns. diff --git a/versioned_docs/version-1.1/toolhive/concepts/registry-criteria.mdx b/versioned_docs/version-1.1/toolhive/concepts/registry-criteria.mdx new file mode 100644 index 00000000..ca51933f --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/concepts/registry-criteria.mdx @@ -0,0 +1,166 @@ +--- +title: 'Registry criteria' +description: Criteria for adding MCP servers to the ToolHive registry +--- + +The ToolHive registry is a curated list of MCP servers that meet specific +criteria. We aim to establish a curated, community-auditable list of +high-quality MCP servers through clear, observable, and objective criteria. + +## Contribute to the registry + +If you have an MCP server that you'd like to add to the ToolHive registry, you +can +[open an issue](https://github.com/stacklok/toolhive-catalog/issues/new?template=add-an-mcp-server.md) +or submit a pull request to the +[toolhive-catalog](https://github.com/stacklok/toolhive-catalog) repository. The +ToolHive team will review your submission and consider adding it to the +registry. + +Criteria for adding an MCP server to the ToolHive registry are outlined below. +These criteria ensure that the servers in the registry meet the standards of +security, quality, and usability that ToolHive aims to uphold. + +Registry entries are defined in the +[toolhive-catalog](https://github.com/stacklok/toolhive-catalog) repository. + +## Criteria for MCP servers + +### Open source requirements + +- Must be fully open source with no exceptions +- Source code must be publicly accessible +- Must use an acceptable open source license (see + [Acceptable licenses](#acceptable-licenses)) + +### Security + +- Software provenance verification (Sigstore, GitHub Attestations) +- SLSA compliance level assessment +- Pinned dependencies and GitHub Actions +- Published Software Bill of Materials (SBOMs) + +### Continuous integration + +- Automated dependency updates (Dependabot, Renovate, etc.) +- Automated security scanning +- CVE monitoring +- Code linting and quality checks + +### Repository metrics + +- Repository stars and forks +- Commit frequency and recency +- Contributor activity +- Issue and pull request statistics + +### API compliance + +- Full MCP API specification support +- Implementation of all required endpoints (tools, resources, etc.) +- Protocol version compatibility + +### Tool stability + +- Version consistency +- Breaking change frequency +- Backward compatibility maintenance + +### Code quality + +- Presence of automated tests +- Test coverage percentage +- Quality CI/CD implementation +- Code review practices + +### Documentation + +- Basic project documentation +- API documentation +- Deployment and operation guides +- Regular documentation updates + +### Release process + +- Established CI-based release process +- Regular release cadence +- Semantic versioning compliance +- Maintained changelog + +### Community health + +#### Responsiveness + +- Active maintainer engagement +- Regular commit activity +- Timely issue and pull request responses (issues open 3-4 weeks without + response is a red flag) +- Bug resolution rate +- User support quality + +#### Community strength + +- Project backing (individual vs. organizational) +- Number of active maintainers +- Contributor diversity +- Corporate or foundation support +- Governance model maturity + +### Security requirements + +#### Authentication and authorization + +- Secure authentication mechanisms +- Proper authorization controls +- Standard security protocol support (OAuth, TLS) + +#### Data protection + +- Encryption for data in transit and at rest +- Proper sensitive information handling + +#### Security practices + +- Clear incident response channels +- Security issue reporting mechanisms (email, GHSA, etc.) + +## Future considerations + +### Automated vs manual checks + +- Balance between automated checks (e.g., CI/CD, security scans) and manual + reviews (e.g., community health, documentation quality) +- Automated checks for basic compliance (e.g., license, API support) +- Manual reviews for nuanced aspects (e.g., community strength, documentation + quality) + +### Scoring system + +- **Required**: Essential attributes (significant penalty if missing) +- **Expected**: Typical well-executed project attributes (moderate score impact) +- **Recommended**: Good practice indicators (positive contribution) +- **Bonus**: Excellence demonstrators (pure positive, no penalty for absence) + +### Tiered classifications + +- "Verified" vs "Experimental/Community" designations +- Minimum threshold requirements (stars, maintainers, community indicators) +- Regular re-evaluation frequency for automated checks + +## Acceptable licenses + +The following open source licenses are accepted for MCP servers in the ToolHive +registry: + +### Permissive licenses + +Licenses such as Apache-2.0, MIT, BSD-2-Clause, and BSD-3-Clause allow maximum +flexibility for integration, modification, and redistribution with minimal +restrictions, making MCP servers accessible across all project types and +commercial applications. + +### Excluded licenses + +We exclude copyleft and restrictive licenses such as AGPL, GPL2, and GPL3 to +ensure MCP servers can be freely integrated into various commercial and open +source projects without legal complications or viral licensing requirements. diff --git a/versioned_docs/version-1.1/toolhive/concepts/skills.mdx b/versioned_docs/version-1.1/toolhive/concepts/skills.mdx new file mode 100644 index 00000000..17feced0 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/concepts/skills.mdx @@ -0,0 +1,158 @@ +--- +title: Understanding skills +description: + Learn what skills are in ToolHive, why they exist, and how they relate to MCP + servers. +--- + +A **skill** is a reusable, versioned bundle of instructions, prompts, and +configuration that teaches an AI agent how to perform a specific task. If MCP +servers provide **tools** (the raw capabilities an agent can call), skills +provide the **knowledge** of when, why, and how to use those tools effectively. + +Skills follow the [Agent Skills](https://agentskills.io/) specification, an open +standard supported by a growing number of AI coding agents including Claude +Code, GitHub Copilot, Cursor, OpenCode, and many others. + +## When you would use skills + +Consider these scenarios: + +- **You maintain a shared MCP registry** and want teams to publish reusable + prompt bundles alongside the MCP servers they connect to, so that other + engineers can discover both the tooling and the expertise in one place. +- **You build internal developer tools** and want to package a "code review" + workflow that combines multiple MCP server calls with domain-specific + instructions, then distribute it through a central catalog. +- **You run a platform team** and want to curate a set of approved skills that + your organization's AI agents can use, with clear versioning and status + tracking. + +## How skills relate to MCP servers + +MCP servers expose tools; skills consume them. A skill might reference one or +more tools from one or more MCP servers, wrapping them in a higher-level +workflow with context-specific instructions. + +```mermaid +flowchart LR + Skill["Skill\n(workflow + prompts)"] -->|references| MCP["MCP Server(s)\n(raw tools)"] +``` + +When you publish skills to the ToolHive Registry for team-wide discovery, they +are stored under a separate extensions API path +(`/{registryName}/v0.1/x/dev.toolhive/skills`, where `{registryName}` is the +name of your registry). They are not intermixed with MCP server entries. + +## Skill structure + +A skill is a directory containing a `SKILL.md` file (required) plus optional +supporting files: + +```text +my-skill/ +├── SKILL.md # Required: metadata + instructions +├── scripts/ # Optional: executable code +├── references/ # Optional: documentation +└── assets/ # Optional: templates, resources +``` + +The `SKILL.md` file contains YAML frontmatter with metadata followed by Markdown +instructions that the AI agent reads when the skill is activated. + +### SKILL.md frontmatter + +| Field | Required | Description | +| --------------- | -------- | ----------------------------------------------------- | +| `name` | Yes | Lowercase letters, numbers, and hyphens (2-64 chars). | +| | | Must match the directory name. | +| `description` | Yes | What the skill does and when to use it (max 1024 | +| | | chars). | +| `license` | No | SPDX license identifier (e.g., `Apache-2.0`). | +| `compatibility` | No | Environment requirements (max 500 chars). | +| `metadata` | No | Arbitrary key-value pairs. | +| `allowed-tools` | No | List of pre-approved tools. | +| `version` | No | Semantic version (e.g., `1.0.0`). ToolHive extension; | +| | | the base spec uses `metadata` for versioning. | + +Example: + +```yaml title="SKILL.md" +--- +name: code-review +description: >- + Performs structured code reviews using best practices. Use when reviewing pull + requests or code changes. +version: 1.0.0 +license: Apache-2.0 +--- +``` + +For the complete format specification, see +[agentskills.io/specification](https://agentskills.io/specification). + +### Naming conventions + +- **Name**: Use kebab-case identifiers. For example, `code-review`, + `deploy-checker`, `security-scan`. The name must match the parent directory + name. +- **Version**: Use semantic versioning (e.g., `1.0.0`, `2.1.3`). + +When publishing skills to a ToolHive Registry Server, additional fields apply: + +- **Namespace**: Use reverse-DNS notation (e.g., `io.github.your-org`). This + prevents naming collisions across organizations. +- **Version**: Required when publishing to the registry. +- **Status**: One of `active`, `deprecated`, or `archived` (case insensitive). + +### Package types + +Skills can reference distribution packages in two formats: + +- **OCI**: Container registry references with an identifier, digest, and media + type +- **Git**: Repository references with a URL, ref, commit, and optional subfolder + +## Versioning + +The registry stores multiple versions of each skill and maintains a "latest" +pointer. When you publish a new version, the registry automatically updates the +latest pointer if the new version is newer than the current latest. Publishing +an older version does not change the latest pointer. + +You can retrieve a specific version or request `latest` to get the most recent +one. + +## How skills are discovered + +AI agents discover skills by reading from their skills directories. When you +install a skill with the ToolHive CLI, the `SKILL.md` file is written to the +appropriate directory for your AI client. The agent loads the skill's `name` and +`description` at startup, and activates the full instructions when a task +matches. + +Skills support two installation scopes: + +- **User scope**: Available across all your projects (installed to your home + directory) +- **Project scope**: Available only in a specific project (installed to the + project directory) + +## Distribution + +Skills can be distributed as: + +- **OCI artifacts**: Packaged as container registry artifacts for versioned + distribution through registries like GitHub Container Registry +- **Git repositories**: Referenced by URL with optional branch/tag and subfolder +- **Registry entries**: Published to a ToolHive Registry Server for centralized + discovery and installation by name + +## Next steps + +- [Manage agent skills](../guides-cli/skills-management.mdx) with the ToolHive + CLI +- [Manage skills in the registry](../guides-registry/skills.mdx) through the + Registry Server API +- [Agent Skills specification](https://agentskills.io/specification) for the + complete format reference diff --git a/versioned_docs/version-1.1/toolhive/concepts/tool-optimization.mdx b/versioned_docs/version-1.1/toolhive/concepts/tool-optimization.mdx new file mode 100644 index 00000000..35b5c85b --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/concepts/tool-optimization.mdx @@ -0,0 +1,260 @@ +--- +title: Optimizing LLM context with tool filtering and overrides +sidebar_label: Optimizing LLM context +description: + Understanding when and why to use tool filtering and overrides to reduce + context pollution and improve AI performance. +--- + +When AI assistants interact with MCP servers, they receive information about +every available tool. While having many tools provides flexibility, it can also +create problems. This guide explains how tool filtering and overrides help you +optimize your AI's context window for better performance and more focused +results. + +## The context pollution problem + +Modern AI clients work by analyzing all available tools and selecting the most +appropriate one for each task. This selection process happens in the AI model's +context, which means every tool's name, description, and schema consumes tokens +and processing time. + +Consider what happens when you connect an AI client to multiple MCP servers: + +- A GitHub server might expose 30+ tools for repositories, issues, pull + requests, and more +- A filesystem server adds another 10+ tools for file operations +- A database server contributes 20+ tools for queries and schema management +- Additional servers for Slack, Jira, monitoring systems, and other integrations + +Before you know it, your AI client is evaluating 100+ tools for every request. + +### Why this matters + +When your AI receives too many tools, several problems emerge: + +**Performance degradation**: More tools mean longer processing time as the AI +model evaluates each option. Tool selection becomes a bottleneck, especially for +complex queries. + +**Higher costs**: Every tool description consumes tokens. In token-based pricing +models, exposing unnecessary tools directly increases your costs for every AI +interaction. + +**Reduced accuracy**: When faced with many similar tools, AI models sometimes +choose incorrectly. A client might use a production database tool when it should +use a development one, or select a write operation when a read would suffice. + +The solution is selective tool exposure - showing your AI only the tools it +actually needs. + +## Tool filtering + +Tool filtering restricts which tools from an MCP server are available to +clients. Think of it as creating a curated subset of functionality for specific +use cases. + +### How filtering works + +ToolHive uses an allow-list approach. When you specify a filter, only the tools +you explicitly list become available. The filtering happens at the HTTP proxy +level, so: + +- The AI only sees allowed tools in its tool list +- Attempts to call filtered tools result in errors +- The backend MCP server remains unchanged + +An empty filter means all tools are available. Once you add any tool to the +filter, only listed tools are exposed. + +### When to use filtering + +Filtering makes sense in several scenarios: + +#### Improving AI tool selection + +When an MCP server exposes many tools but you only need a subset, filtering +improves the AI's ability to choose correctly. For example, enable only pull +request tools from the GitHub server when doing code review, or limit a file +system server to just `read_file` and `list_directory` for a documentation +assistant. Removing irrelevant options helps the AI make more confident +selections. + +#### Limiting access to safe operations + +An MCP server for database access might include both read and write operations. +During development or analysis, you might want to expose only read operations +like `query` and `list_tables`, while filtering out write operations like +`insert`, `update`, and `delete` that modify data or perform destructive +operations. + +#### Creating role-specific tool sets + +Different team members need different capabilities. Junior developers might get +filtered access to safe operations, while senior developers see the full tool +set. Security-sensitive tools like deployment commands might be filtered for +most users but available to DevOps engineers. + +#### Compliance and governance + +When organizational policies restrict certain operations, you can enforce policy +by only exposing approved tools, even if the underlying MCP server provides more +capabilities. + +## Tool overrides + +Tool overrides let you rename tools and update their descriptions without +modifying the backend MCP server. This is particularly valuable when tool names +are unclear or when combining multiple servers. + +### How overrides work + +Overrides maintain a bidirectional mapping between original and user-facing +names. When your AI sees the tool list, it receives the overridden names and +descriptions. When it calls a tool, ToolHive translates the user-facing name +back to the original name for the backend server. + +You can override either the name, the description, or both for each tool. + +### When to use overrides + +Overrides solve several common problems: + +#### Preventing name conflicts + +When combining multiple MCP servers through Virtual MCP Server or running +similar servers for different purposes, naming conflicts are common. Both GitHub +and Jira might have a `create_issue` tool. Overriding these to +`github_create_issue` and `jira_create_issue` eliminates ambiguity. + +When you run the same MCP server multiple times with different configurations, +tool names become identical. For example, running the GitHub server twice (once +for your company's organization and once for open source contributions) requires +renaming tools to `github_company_create_pr` and `github_oss_create_pr` to make +the distinction clear. + +#### Adding context and clarity + +Tool names and descriptions can be improved to provide environment-specific or +use-case-specific information. Renaming `deploy` to `deploy_to_staging` versus +`deploy_to_production` makes the destination explicit and reduces mistakes. +Similarly, you can update descriptions from generic text like "Deploy +application" to specific guidance like "Deploy to staging environment - +auto-rollback enabled." + +## Combining filters and overrides + +Filtering and overrides work together, but understanding their interaction is +important: **filters apply to user-facing names after overrides**. + +This means when you override a tool name, you must use the new name in your +filter list, not the original name. + +### Pattern: Secure subset with clear names + +Start by overriding technical names to be more intuitive, then filter to only +safe operations. + +```json +{ + "toolsOverride": { + "exec_raw_sql": { + "name": "run_database_query", + "description": "Execute read-only SQL queries against the staging database" + }, + "write_table": { + "name": "update_database", + "description": "Modify staging database tables (use with caution)" + } + } +} +``` + +Then filter using the new names: + +```bash +thv run --tools-override overrides.json --tools run_database_query my-db-server +``` + +**Why this works**: Clear names guide the AI while filtering enforces safety by +making destructive operations unavailable. + +### Pattern: Environment-specific configurations + +Different environments need different tool access. In development, you might +expose many tools for flexibility. In production, filter to essential tools +only. + +Your development configuration could expose all tools with friendly names +through overrides. Your production configuration uses the same overrides for +consistency but adds strict filtering to expose only read and monitoring tools, +blocking any write or deployment operations. + +**Why this works**: Consistent naming across environments with +environment-specific filtering prevents accidents while maintaining flexibility. + +### Pattern: Multi-server aggregation + +When using [Virtual MCP Server](vmcp.mdx) to combine multiple MCP servers, +overrides prevent conflicts and improve clarity: + +```json +{ + "toolsOverride": { + "search": { + "name": "github_search", + "description": "Search GitHub repositories and code" + } + } +} +``` + +You can override the `search` tool from different servers to `github_search`, +`jira_search`, and `confluence_search`. Then filter each server to its relevant +tools, creating a clean, conflict-free tool set. + +**Why this works**: Prefixes eliminate ambiguity about which server a tool +targets, while filtering prevents context overload from aggregating many +services. + +## Trade-offs to consider + +While these optimization features provide significant benefits, they also +introduce complexity: + +**Configuration and maintenance overhead**: Filters and overrides require +ongoing maintenance. When MCP servers update their tools, you'll need to adjust +your configurations. When using both features together, remember that filters +apply to overridden names, not original names. + +**Flexibility vs. safety**: Aggressive filtering makes it harder to access tools +you occasionally need. You may find yourself creating exceptions or +reconfiguring access. The more you optimize, the less flexible your system +becomes. + +**Discovery and documentation**: When tools are filtered or renamed, team +members may not realize what capabilities exist. Clear documentation becomes +essential when your visible tools don't match what the MCP server actually +provides. + +Start simple and add complexity only where it provides clear value. + +## Best practices + +**Start minimal**: Begin with a focused tool set and expand as you discover +needs, rather than filtering down from everything. + +**Be specific**: When overriding names and descriptions, provide clear, +context-specific information that helps the AI understand when to use each tool. + +## Related information + +Now that you understand when and why to use tool filtering and overrides, learn +how to configure them: + +- [Customize tools (CLI)](../guides-cli/run-mcp-servers.mdx) +- [Customize tools (UI)](../guides-ui/customize-tools.mdx) +- [Customize tools (Kubernetes)](../guides-k8s/customize-tools.mdx) +- [MCPToolConfig CRD reference](../reference/crd-spec.md) +- [Virtual MCP Server tool aggregation](../guides-vmcp/tool-aggregation.mdx) +- [Optimize tool discovery in vMCP](../guides-vmcp/optimizer.mdx) diff --git a/versioned_docs/version-1.1/toolhive/concepts/vmcp.mdx b/versioned_docs/version-1.1/toolhive/concepts/vmcp.mdx new file mode 100644 index 00000000..b6c6069f --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/concepts/vmcp.mdx @@ -0,0 +1,176 @@ +--- +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), 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 + +**Before vMCP**: Engineers manage 10+ separate MCP server connections, each with +its own authentication, manually coordinate multi-step workflows across systems, +and repeatedly configure the same parameters. + +**With vMCP**: Connect once to a unified endpoint that aggregates all backend +MCP servers, execute complex multi-system workflows declaratively, and use +pre-configured tools with sensible defaults. + +## Core value propositions + +vMCP delivers four key benefits: + +1. **Reduce complexity**: Many connections become one, dramatically simplifying + configuration +2. **Speed up workflows**: Parallel execution across systems instead of + sequential calls +3. **Improve security**: Centralized authentication and authorization with a + two-boundary model +4. **Enable reusability**: Define workflows once, use them everywhere +5. **Optimize tool discovery**: Reduce token usage by replacing all tool + definitions with two lightweight search-and-call primitives + +## Key capabilities + +### Multi-server aggregation + +Managing 10-20+ MCP server connections is overwhelming. Each server needs its +own configuration, authentication, and maintenance. vMCP aggregates all backend +MCP servers into one endpoint with automatic conflict resolution. + +vMCP supports two types of backends: + +- **MCPServer**: Container-based MCP servers running in your Kubernetes cluster +- **MCPRemoteProxy**: Proxies to external remote MCP servers (SaaS platforms, + third-party services, or MCP servers hosted outside your cluster) + +:::note[MCPRemoteProxy support] + +MCPRemoteProxy support in vMCP is currently in development. vMCP can discover +MCPRemoteProxy backends, but authentication between vMCP and MCPRemoteProxy is +not yet fully implemented. MCPServer backends work fully with vMCP. + +::: + +This architecture enables combining internal tools with external SaaS MCP +endpoints in a single unified interface. + +**Example scenario**: An engineering team needs access to 8 backend servers +(GitHub, Jira, Slack, Confluence, PagerDuty, Datadog, AWS, and internal company +docs). Some are container-based MCPServer resources running in the cluster, +while others are remote SaaS MCP endpoints accessed via MCPRemoteProxy. 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. + +When multiple backend MCP servers have tools with the same name (for example, +both GitHub and Jira have `create_issue`), vMCP automatically prefixes them: +`github_create_issue`, `jira_create_issue`. You can also define custom names for +clarity. + +### Multi-step workflows (composition) + +Real-world tasks span multiple systems and require manual orchestration. vMCP +lets you define declarative workflows with parallel execution, conditionals, +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. 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 + +Third-party MCP servers often have generic names, descriptions, and unrestricted +parameters. vMCP lets you filter, rename, and wrap tools without modifying the +upstream servers. + +**Security policy enforcement**: You can restrict a fetch tool to internal +domains only (`*.company.com`), validate URLs before calling the backend, and +provide clear error messages for policy violations. + +**Simplified interfaces**: A complex tool like AWS EC2 might have 20+ +parameters, but your frontend team only needs 3. You can create a simplified +wrapper that uses instance names instead of IDs and pre-fills safe defaults for +all other parameters. + +### Parameter defaults and pre-configuration + +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**: 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**: 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 authentication is needed for clients versus backend MCP servers, 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 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. + +## When to use vMCP + +### Good fit + +- Teams managing 5+ MCP servers (local or remote) +- Tasks requiring coordination across multiple systems +- Centralized authentication and authorization requirements +- Workflows that should be reusable across the team +- Security policies that need centralized enforcement +- Aggregating external SaaS MCP servers with internal tools + +### Not needed + +- Single MCP server usage +- Simple, one-step operations +- No orchestration requirements + +## Summary + +vMCP transforms MCP from individual servers into a unified orchestration +platform. It aggregates both container-based MCPServer resources and remote +MCPRemoteProxy backends into a single endpoint. The use cases range from simple +aggregation to complex workflows with approval gates, making it valuable for +teams managing multiple MCP servers. + +## Next steps + +- [Try the Quickstart](../guides-vmcp/quickstart.mdx) to deploy your first vMCP + on a Kubernetes cluster +- [Learn how to deploy vMCP](../guides-vmcp/intro.mdx) for a full overview of + configuration and architecture + +## Related information + +- [Configure authentication](../guides-vmcp/authentication.mdx) +- [Tool aggregation and conflict resolution](../guides-vmcp/tool-aggregation.mdx) +- [Composite tools and workflows](../guides-vmcp/composite-tools.mdx) +- [Optimize tool discovery](../guides-vmcp/optimizer.mdx) +- [Proxy remote MCP servers](../guides-k8s/remote-mcp-proxy.mdx) diff --git a/versioned_docs/version-1.1/toolhive/contributing.mdx b/versioned_docs/version-1.1/toolhive/contributing.mdx new file mode 100644 index 00000000..1b061c71 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/contributing.mdx @@ -0,0 +1,194 @@ +--- +title: Contributing to ToolHive +sidebar_label: Contributing +description: Learn how to contribute to ToolHive and its ecosystem of projects. +--- + +Thank you for your interest in contributing to ToolHive! This page provides an +overview of the project's architecture and links to resources for contributing +to each component. + +## Project overview + +ToolHive is composed of several independently developed components, each with +its own codebase, contributing guide, and development workflow. The components +work together to provide a complete platform for running and managing Model +Context Protocol (MCP) servers. + +All ToolHive components are open source and licensed under the Apache 2.0 +license. + +## Project components + +### ToolHive CLI, API, and Kubernetes Operator + +The core ToolHive repository contains the command-line interface, API server, +and Kubernetes operator. These components share a common codebase and provide +the runtime environment for deploying and managing MCP servers. + +**Repository**: [stacklok/toolhive](https://github.com/stacklok/toolhive) + +**Key resources**: + +- [Contributing guide](https://github.com/stacklok/toolhive/blob/main/CONTRIBUTING.md) +- [Developer documentation](https://github.com/stacklok/toolhive/blob/main/docs/README.md) +- [Kubernetes Operator developer guide](https://github.com/stacklok/toolhive/blob/main/cmd/thv-operator/README.md) +- [Issue tracker](https://github.com/stacklok/toolhive/issues) + +### ToolHive desktop UI + +The ToolHive Desktop UI is a cross-platform application that provides a +graphical interface for discovering, installing, and managing MCP servers. It's +built with TypeScript and Electron, and runs on macOS, Windows, and Linux. + +**Repository**: +[stacklok/toolhive-studio](https://github.com/stacklok/toolhive-studio) + +**Key resources**: + +- [Contributing guide](https://github.com/stacklok/toolhive-studio/blob/main/CONTRIBUTING.md) +- [Developer documentation](https://github.com/stacklok/toolhive-studio/blob/main/docs/README.md) +- [Issue tracker](https://github.com/stacklok/toolhive-studio/issues) + +### ToolHive Cloud UI + +The ToolHive Cloud UI is a web application for visualizing MCP servers running +in user infrastructure with easy URL copying for integration with AI agents. +Built with Next.js and TypeScript, this is an experimental project that is +actively being developed and tested. + +**Repository**: +[stacklok/toolhive-cloud-ui](https://github.com/stacklok/toolhive-cloud-ui) + +**Key resources**: + +- [Contributing guide](https://github.com/stacklok/toolhive-cloud-ui/blob/main/CONTRIBUTING.md) +- [Issue tracker](https://github.com/stacklok/toolhive-cloud-ui/issues) + +### Registry server + +The Registry Server is an API server that implements the official MCP Registry +API. It provides standardized access to MCP servers from multiple backends, +including file-based and other API-compliant registries. + +**Repository**: +[stacklok/toolhive-registry-server](https://github.com/stacklok/toolhive-registry-server) + +**Key resources**: + +- [Contributing guide](https://github.com/stacklok/toolhive-registry-server/blob/main/CONTRIBUTING.md) +- [Issue tracker](https://github.com/stacklok/toolhive-registry-server/issues) + +### Built-in registry + +The built-in registry contains the curated list of MCP servers available in +ToolHive. If you want to add a new MCP server to the registry, this is the +repository to contribute to. + +**Repository**: +[stacklok/toolhive-catalog](https://github.com/stacklok/toolhive-catalog) + +**Key resources**: + +- [Contributing guide](https://github.com/stacklok/toolhive-catalog/blob/main/CONTRIBUTING.md) +- [Registry inclusion criteria](./concepts/registry-criteria.mdx) +- [Submit a new MCP server](https://github.com/stacklok/toolhive-catalog/issues/new?template=add-an-mcp-server.md) +- [Issue tracker](https://github.com/stacklok/toolhive-catalog/issues) + +### Dockyard + +Dockyard is a tool that packages MCP servers into containers. It handles the +containerization process, making it easy to run MCP servers in isolated +environments. + +**Repository**: [stacklok/dockyard](https://github.com/stacklok/dockyard) + +**Key resources**: + +- [Contributing guide](https://github.com/stacklok/dockyard/blob/main/CONTRIBUTING.md) +- [Issue tracker](https://github.com/stacklok/dockyard/issues) + +### MCP Optimizer + +The MCP Optimizer acts as an intelligent intermediary between AI clients and MCP +servers. It provides tool discovery, unified access to multiple MCP servers +through a single endpoint, and intelligent routing. The optimizer reduces token +usage by narrowing down the toolset to only relevant tools for each request. + +**Repository**: +[StacklokLabs/mcp-optimizer](https://github.com/StacklokLabs/mcp-optimizer) + +**Key resources**: + +- [Contributing guide](https://github.com/StacklokLabs/mcp-optimizer/blob/main/CONTRIBUTING.md) +- [Usage tutorial](./tutorials/mcp-optimizer.mdx) +- [Documentation](https://github.com/StacklokLabs/mcp-optimizer/tree/main/docs) +- [Issue tracker](https://github.com/StacklokLabs/mcp-optimizer/issues) + +### Documentation website + +The documentation website (this site) is built with Docusaurus and contains +guides, tutorials, and reference documentation for all ToolHive components. + +**Repository**: +[stacklok/docs-website](https://github.com/stacklok/docs-website) + +**Key resources**: + +- [Contributing guide](https://github.com/stacklok/docs-website/blob/main/CONTRIBUTING.md) +- [Writing style guide](https://github.com/stacklok/docs-website/blob/main/STYLE-GUIDE.md) +- [Issue tracker](https://github.com/stacklok/docs-website/issues) + +## Ways to contribute + +We welcome contributions of all kinds, and no contribution is too small. Whether +you're fixing a typo, improving documentation, or adding a major feature, your +help is appreciated. First-time contributors are especially welcome! + +Here are some ways you can contribute to ToolHive: + +- **Code**: Fix bugs, add features, or improve performance in any of the project + components. +- **Documentation**: Write guides, improve existing docs, add examples, or fix + typos. +- **Testing**: Test new features, report bugs, or contribute automated tests. +- **Bug triage**: Help review and reproduce issues reported by others. +- **Examples and tutorials**: Create sample projects or write tutorials showing + how to use ToolHive. +- **MCP servers**: Submit new MCP servers to the registry or improve existing + ones. +- **Community support**: Help answer questions in GitHub issues or on Discord. +- **Design and UX**: Contribute to the UI design or suggest improvements to the + user experience. + +## Getting started + +Ready to contribute? Here are some ways to get involved: + +1. **Explore the codebase**: Browse the repositories that interest you and read + through the contributing guides to understand the development workflow. + +2. **Find an issue**: Look for issues labeled `good first issue` or + `help wanted` in the issue trackers. These are great starting points for new + contributors. + +3. **Join the community**: Connect with other contributors in the + [Stacklok Discord](https://discord.gg/stacklok) community. The + `#toolhive-developers` channel is dedicated to technical discussions. + +4. **Report bugs**: If you find a bug, open an issue in the appropriate + repository with a clear description and steps to reproduce. + +5. **Suggest features**: Have an idea for a new feature? Open an issue to + discuss it with the maintainers before starting work. + +## Code of conduct + +All contributors are expected to follow the +[Stacklok Code of Conduct](https://github.com/stacklok/toolhive/blob/main/CODE_OF_CONDUCT.md). +We're committed to providing a welcoming and inclusive environment for everyone. + +## Additional resources + +- [Frequently asked questions](./faq.mdx) +- [Stacklok Discord community](https://discord.gg/stacklok) diff --git a/versioned_docs/version-1.1/toolhive/enterprise.mdx b/versioned_docs/version-1.1/toolhive/enterprise.mdx new file mode 100644 index 00000000..b949af93 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/enterprise.mdx @@ -0,0 +1,322 @@ +--- +title: Stacklok Enterprise +description: Stacklok Enterprise offerings for ToolHive +--- + +import HubSpotForm from '@site/src/components/HubSpotForm'; +import Heading from '@theme/Heading'; +import BrandedList from '@site/src/components/BrandedList'; + + + A hardened and production-ready distribution of ToolHive Community + + +Securely scale MCP servers across your enterprise with Stacklok Enterprise's +signed binaries, hardened images, formal semantic versioning, backported +security patches, and turnkey identity provider integrations. Kubernetes native +and LLM agnostic. Self-hosted in your environment, governed by your policies, no +vendor lock-in. + + + +--- + +

+ Running in production at major financial services, technology, and software + companies,
+ including Fortune 500 and Global 2000 enterprises +

+ +--- + +## When Community isn't enough + +Teams typically move to Stacklok Enterprise when they hit one of these walls: + + + +- Developers are bringing their own MCP servers to work — shadow AI is spreading + and there is no central control +- Your organization has multiple coding assistants and AI agents that need + access to business context +- Your security or compliance team is asking how MCP servers are authenticated, + audited, and patched +- You need SSO and IdP integration (Okta, Entra ID) across your organization +- You are running MCP in production and need SLA-backed support for incidents +- You need centralized governance and policy enforcement across multiple teams + or business units +- Your environment requires a semantically versioned, supply-chain-attested + distribution rather than continuous rolling release + + + +Recognizing these challenges in your organization? +[Schedule a demo](#schedule-a-demo) to see how Stacklok Enterprise addresses +them. + +--- + +## ToolHive Community vs. Stacklok Enterprise + +### Distribution & packaging + +| Capability | Community | Enterprise | +| :-------------------------------------------------------- | :--------: | :----------------------------------------: | +| ToolHive core platform | ✓ | ✓ | +| Release model | Continuous | Semantically versioned (MAJOR.MINOR.PATCH) | +| SigStore Cosign package signing with SBOM | ✓ | ✓ | +| Patch versions retained for bugfixes and security updates | — | ✓ | +| Scanning attestations | — | ✓ | +| SLSA build provenance | — | ✓ | + +### Security and supply chain + +| Capability | Community | Enterprise | +| :------------------------------------------------------- | :-------: | :--------: | +| Basic scanning (Trivy, unit tests, integration tests) | ✓ | ✓ | +| Static analysis on every release (attested via SigStore) | — | ✓ | +| Autonomous pen testing on every minor release | — | ✓ | +| Hardened container base images (Chainguard or equiv.) | — | ✓ | +| Proactive notification of vulnerabilities | — | ✓ | +| CVEs addressed within SLO with responsible disclosure | — | ✓ | +| All Sev 0-3 vulnerabilities backported as patch updates | — | ✓ | + +### Auth, identity & governance + +| Capability | Community | Enterprise | +| :---------------------------------------------------- | :-------: | :--------: | +| Basic authentication | ✓ | ✓ | +| Policy-as-code engine (CEDAR) | ✓ | ✓ | +| Audit logging & compliance reporting | ✓ | ✓ | +| Built-in IdP integration (Okta, Entra ID) | — | ✓ | +| IdP group → ToolHive role mapping | — | ✓ | +| Canonical policy packs (read-only, full CRUD, custom) | — | ✓ | +| Token exchange & credential brokering | — | ✓ | + +### Enterprise UI & management + +| Capability | Community | Enterprise | +| :------------------------------------------------- | :-------: | :--------: | +| ToolHive CLI | ✓ | ✓ | +| Usage telemetry & analytics (OpenTelemetry) | ✓ | ✓ | +| Enterprise MCP registry server and catalog | ✓ | ✓ | +| Enterprise Cloud UI (full CRUD management console) | — | ✓ | +| Hardened Desktop UI (enterprise lockdown controls) | — | ✓ | + +### Versioning, maintenance & support + +| Capability | Community | Enterprise | +| :--------------------------------------------- | :-------: | :--------: | +| Latest release | ✓ | ✓ | +| Supported versions: LATEST, LATEST-1, LATEST-2 | — | ✓ | +| Community support (GitHub) | ✓ | ✓ | +| Dedicated support with SLA | — | ✓ | +| Proactive security advisories | — | ✓ | +| Onboarding & integration assistance | — | ✓ | + +### Enterprise Connectors (MCP Servers) + +| Attribute | Community | Enterprise | +| :------------------------------------------ | :---------: | :----------------------------------: | +| Base image | Open source | Chainguard or equivalent | +| Signing & attestations | — | SigStore signed with SLSA provenance | +| Customized tools (tuned to agent workflows) | — | ✓ | +| Streamable HTTP transport | — | ✓ | +| SBOM & dependency vetting | — | ✓ | +| Qualified for target workload | — | ✓ | +| Maintained on enterprise release cadence | — | ✓ | +| Backported security patches | — | ✓ | + +Seen enough to want a closer look? [Schedule a demo](#schedule-a-demo) to walk +through the capabilities that matter most to your team. + +--- + +## Product offerings + +Stacklok aims to keep pricing and licensing simple. Stacklok Enterprise and its +Enterprise Connectors are licensed as an annual subscription. Professional +services are priced based on time and materials. + +| SKU | Description | Pricing Model | +| :------------------------------- | :----------------------------------------------------------------------------------------------------------------------------- | :---------------------------------: | +| **Stacklok Enterprise Platform** | Enterprise licensed distribution of ToolHive with Cloud UI, Desktop UI, IdP integration, policy engine, and SLA-backed support | Annual subscription | +| **Enterprise Connectors** | Production-ready connectors, maintained on enterprise release cadence | Annual subscription (per connector) | +| **Professional Services** | Extended integration, policy configuration, additional IdP onboarding, connector development | Time & materials | + +Ready to discuss what the right package looks like for your organization? +[Schedule a demo](#schedule-a-demo) to talk through your requirements. + +--- + +## Enterprise Platform Components + +Stacklok Enterprise Platform secures MCP servers across your organization +through its registry, runtime, gateway, and portal. + +### Registry: No more fighting shadow AI + +| The source of truth for approved MCP servers within the enterprise. | +| :----------------------------------------------------------------------- | +| Integrate with the official MCP registry | +| Add custom MCP servers and skills | +| Group servers based on role or use case | +| Manage your registry with an API-driven interface | +| Verify provenance and sign servers with built-in security controls | +| Preset configurations and permissions for a frictionless user experience | + +### Runtime: Kubernetes-native deployment + +| Deploy, run, and manage MCP servers in Kubernetes with security guardrails. | +| :-------------------------------------------------------------------------- | +| Deploy MCP servers in the cloud via Kubernetes | +| Run MCP servers locally via Docker or Podman | +| Proxy remote MCP servers securely for unified management | +| Kubernetes Operator for fleet and resource management | +| Leverage OpenTelemetry for centralized monitoring and audit logging | + +### Gateway: Single endpoint, full control + +| Intelligent MCP gateway for authentication, authorization, and policy enforcement. | +| :--------------------------------------------------------------------------------------- | +| Integrate with your IdP for SSO (OIDC/OAuth compatible) | +| Build composite tools that orchestrate multiple tools in parallel or sequential chains | +| Customize and filter tools and descriptions | +| Reduce context bloat and token usage | +| Connect with local clients like Claude Desktop, Cursor, and Visual Studio Code (VS Code) | + +### Portal: Self-service with guardrails + +| Custom UI for teams to discover, deploy and manage approved MCP servers. | +| :----------------------------------------------------------------------- | +| Cross-platform desktop app and web-based cloud UI | +| Make it easy for admins to curate MCP servers and tools | +| Automate server discovery | +| Install MCP servers with a single click | +| Compatible with hundreds of AI clients | + +Ready to see how the platform works in your environment? +[Start a proof of concept](#validate-stacklok-enterprise-in-your-environment) to +take the next step. + +--- + +## Validate Stacklok Enterprise in your environment + +Stacklok helps you validate Stacklok Enterprise in your environment at your pace +with forward-deployed engineering support. + + + +--- + +## Frequently asked questions + +
+How does Stacklok Enterprise relate to ToolHive Community? + +ToolHive Community is an open source distribution optimized for individual +developers and pre-production use, making it the right tool for evaluating MCP +and building a proof of concept. Stacklok Enterprise is a separate, hardened +distribution built for production: semantically versioned, with IdP integration, +centralized governance, and SLA-backed support. Moving from Community to +Enterprise is a supported migration where Stacklok provides the enterprise +binaries and dedicated engineering support to take you from proof of concept to +production. +[See the full comparison](#toolhive-community-vs-stacklok-enterprise) or +[learn about the proof of concept engagement](#validate-stacklok-enterprise-in-your-environment). + +
+ +
+What happens to my data if I end my Enterprise contract? + +Your data never leaves your environment. Stacklok Enterprise is fully +self-hosted: you retain complete control over your data and infrastructure, +regardless of contract status. If you end your subscription, you can downgrade +to the open-source version at any time. The only things you lose are access to +Enterprise features, forward-deployed engineers, backported security patches, +and dedicated support. There is zero vendor lock-in. +[Learn more about the product offerings](#product-offerings). + +
+ +
+How long does a typical deployment take? + +Most customers begin to see value in less than 2 weeks of contract signing. +Stacklok works directly with your platform team, and every Enterprise license +includes dedicated engineering support throughout the process. You will need an +existing Kubernetes environment to get started. Timelines are scoped to your +environment, so if your situation is more complex, Stacklok will work at your +pace. +[Learn about the proof of concept engagement](#validate-stacklok-enterprise-in-your-environment). + +
+ +
+Why should I use an MCP platform instead of running MCP servers directly? + +Running MCP servers directly gives you no isolation, no access controls, and no +visibility into what those servers are doing. Stacklok Enterprise addresses this +by running each server in its own container with least-privilege permissions, +encrypting credentials at rest, and tracing every tool call via OpenTelemetry. +Stacklok Enterprise adds centralized governance, IdP-backed authentication, and +audit logging for teams running MCP at scale across their organization. +[Explore the core concepts](./concepts/) to dig deeper into how ToolHive works. + +
+ +
+What AI clients work with Stacklok Enterprise? + +Stacklok Enterprise works with any AI coding assistant or agent that supports +MCP. This includes Claude Code, GitHub Copilot, Cursor, Windsurf, VS Code, Zed, +Cline, Continue, Roo Code, Goose, LM Studio, OpenAI Codex, and many more. Most +clients support automatic configuration so developers can connect without manual +setup. +[See the full client compatibility reference](./reference/client-compatibility.mdx) +for the complete list. + +
+ +
+Can I run custom MCP servers outside the Stacklok registry? + +Yes. Stacklok Enterprise starts with a base registry of vetted, hardened MCP +servers maintained by Stacklok. From there, you have full control to add your +own servers from public package managers, Docker images, remote URLs, or build a +private registry tailored to your organization. You are never limited to +Stacklok's catalog. +[See how to run MCP servers in Kubernetes](./guides-k8s/run-mcp-k8s.mdx) for the +full details. + +
+ +--- + +## Explore ToolHive Community + +:::tip[Not ready for Stacklok Enterprise yet?] + +ToolHive Community is free, open source, and the best way to evaluate MCP before +moving to production. + +[Get started with ToolHive Community →](./index.mdx) + +::: diff --git a/versioned_docs/version-1.1/toolhive/faq.mdx b/versioned_docs/version-1.1/toolhive/faq.mdx new file mode 100644 index 00000000..2492d840 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/faq.mdx @@ -0,0 +1,237 @@ +--- +title: Frequently asked questions +sidebar_label: FAQ +description: Common questions about ToolHive. +--- + +## General questions + +### What is ToolHive? + +ToolHive is an open source platform that simplifies the deployment and +management of Model Context Protocol (MCP) servers. It runs MCP servers in +secure, isolated containers and provides tools to manage them easily. You can +run ToolHive as a graphical desktop app, command-line tool, or Kubernetes +operator. + +### What is the Model Context Protocol (MCP)? + +MCP is a protocol that allows AI applications to connect to external data +sources and tools. It provides a standardized way for AI models to access +real-world context like APIs, source code repositories, databases, and other +systems. Think of it as a bridge between AI models and the external systems +where your data and applications live. + +### Do I need to know how to code to use ToolHive? + +No. ToolHive includes a graphical interface that doesn't require coding +knowledge. However, some MCP servers may require configuration or secrets, such +as API keys, that you'll need to set up. The ToolHive CLI is more technical but +comes with comprehensive documentation. + +### Is ToolHive free to use? + +Yes, ToolHive is open source (Apache 2.0 licensed) and free to use. You can find +the source code on GitHub and use it without any licensing fees. + +### Can I use ToolHive UI and the CLI together? + +Yes, but ToolHive UI manages the CLI automatically. When you install ToolHive +UI, it creates a symlink to its bundled CLI and configures your PATH. You can +use the `thv` command from your terminal—it uses the UI-managed version. + +If you have a standalone CLI installed separately (via Homebrew, WinGet, or +manually), it will conflict with the UI-managed version and show an error. +Uninstall the standalone version to resolve this. + +For more information, see the [CLI access guide](./guides-ui/cli-access.mdx) and +[CLI conflict resolution](./guides-cli/install.mdx#cli-conflict-resolution). + +## Using MCP servers + +### How do I find available MCP servers? + +ToolHive includes a curated registry of verified MCP servers. You can browse +them from the **Registry** tab in the UI or by running `thv registry list` in +the CLI. + +### What MCP servers are available? + +The registry includes servers for common use cases, such as retrieving content +from the web using the +[GoFetch MCP server](https://github.com/StacklokLabs/gofetch), and for popular +services and platforms like: + +- **Atlassian** - Access Jira and Confluence +- **AWS Documentation** - Query AWS service documentation +- **GitHub** - Access repositories, issues, and pull requests +- **Kubernetes** - Interact with Kubernetes clusters via the + [MKP MCP server](https://github.com/StacklokLabs/mkp) +- **MongoDB**, **PostgreSQL**, **Redis** - Connect to databases +- **Notion** - Connect to Notion workspaces +- And many more + +### Can I run MCP servers that aren't in the registry? + +Yes. You can run any MCP server from a container image or source package, even +if it's not in the registry. Just provide the image name or package details when +starting the server using the CLI or UI. See the custom MCP server section in +the [UI guide](./guides-ui/run-mcp-servers.mdx#install-a-custom-mcp-server) and +[CLI guide](./guides-cli/run-mcp-servers.mdx#run-a-custom-mcp-server) for more +details. + +The Kubernetes operator also supports custom MCP servers that are packaged as +container images. + +:::tip + +You can use the ToolHive CLI to run a custom MCP server from a source package +once, then export the Docker image for import into your container registry or +Kubernetes cluster to use it with the operator. + +::: + +## Privacy and data collection + +### Does ToolHive collect any data? + +ToolHive collects anonymous usage metrics to help improve the product. These +metrics include only tool call counts and are completely anonymous. No personal +information, user identifiers, or sensitive data is collected. + +The metrics collection: + +- Is enabled by default +- Only tracks the number of tool calls +- Uses a randomly generated instance ID (not tied to your identity) +- Is automatically disabled in CI environments +- Can be easily disabled + +### How do I disable usage metrics? + +You can opt out of usage metrics collection in two ways: + +**Option 1: Persistent configuration (recommended)** + +Use the ToolHive CLI to disable metrics permanently: + +```bash +thv config usage-metrics disable +``` + +**Option 2: Environment variable** + +Set an environment variable to disable metrics for the current session: + +```bash +export TOOLHIVE_USAGE_METRICS_ENABLED=false +``` + +Once you opt out, ToolHive stops collecting and sending usage metrics. You need +to restart any running servers for the change to take effect. + +## Security and permissions + +### Is it safe to run MCP servers? + +ToolHive runs MCP servers in isolated containers with minimal default +permissions. Each server runs in its own container with restricted access to +your system and network. + +:::tip + +For extra security, review the permission profiles and network isolation options +before running new or untrusted MCP servers. + +::: + +### How does ToolHive handle secrets like API keys? + +ToolHive provides secure secrets management: + +- Secrets are encrypted and stored securely on your system +- They're passed to MCP servers as environment variables +- Secrets never appear in plaintext in configuration files +- Integration with 1Password is also supported + +### Can I control what an MCP server can access? + +Yes. ToolHive uses permission profiles to control: + +- **File system access** - Which directories the server can read or write +- **Network access** - Which hosts and ports the server can connect to + +You can use built-in profiles or create custom ones for specific security +requirements. + +### What's network isolation and when should I use it? + +Network isolation creates a secure network architecture that filters all +outbound connections from MCP servers. Use the `--isolate-network` flag when +running servers that need strict network controls, especially in enterprise +environments. + +## Enterprise and advanced usage + +### Can I use ToolHive in my company? + +Yes. ToolHive is designed for both individual developers and enterprise teams. +The Kubernetes Operator provides centralized management, security controls, and +integration with existing infrastructure for enterprise deployments. + +### How do I deploy ToolHive in Kubernetes? + +Use the ToolHive Kubernetes Operator to deploy and manage MCP servers as +Kubernetes resources. See the [Kubernetes guides](./guides-k8s/index.mdx) for +detailed instructions. The Kubernetes operator is currently experimental. + +### Can I run my own custom MCP servers? + +Yes. You can run custom MCP servers from Docker images or source packages. +ToolHive supports: + +- Docker images from public or private registries +- Source packages from package managers like npm, PyPI, or Go modules + +### How do I get my MCP server added to the ToolHive registry? + +The ToolHive registry has specific +[inclusion criteria](./concepts/registry-criteria.mdx), such as being open +source, following good security practices, and maintaining code quality. Review +the criteria and +[submit your server for consideration](https://github.com/stacklok/toolhive-catalog/issues/new?template=add-an-mcp-server.md). + +### Can I use ToolHive behind a corporate firewall? + +Yes. ToolHive supports corporate environments with: + +- Custom CA certificate configuration for TLS inspection +- Network isolation and permission profiles +- Integration with secret management systems like 1Password + +## Getting help + +### Where can I get help if I'm stuck? + +- **Documentation** - Check the comprehensive guides and reference documentation +- **GitHub Issues** - Report bugs or request features on the + [ToolHive GitHub repository](https://github.com/stacklok/toolhive/issues) +- **Discord Community** - Join the + [Stacklok Discord](https://discord.gg/stacklok) for community support +- **Troubleshooting sections** - Each guide includes troubleshooting tips for + common issues + +### How do I report a bug or request a feature? + +Open an issue in the appropriate GitHub repository: + +- [**ToolHive UI**](https://github.com/stacklok/toolhive-studio/issues) - For + issues specific to the graphical desktop app +- [**ToolHive CLI & Kubernetes**](https://github.com/stacklok/toolhive/issues) - + For issues related to the CLI tool or Kubernetes operator + +### Is there a community I can join? + +Yes! Join the [Stacklok Discord community](https://discord.gg/stacklok) to +connect with other ToolHive users, ask questions, and share your experiences. +There's a dedicated `#toolhive-developers` channel for technical discussions. diff --git a/versioned_docs/version-1.1/toolhive/guides-cli/advanced-cicd.mdx b/versioned_docs/version-1.1/toolhive/guides-cli/advanced-cicd.mdx new file mode 100644 index 00000000..5db353f8 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-cli/advanced-cicd.mdx @@ -0,0 +1,324 @@ +--- +title: Advanced CI/CD patterns +description: + Advanced CI/CD patterns for building and deploying MCP server containers with + ToolHive. +--- + +This guide covers advanced CI/CD patterns for building MCP server containers +using ToolHive's [`thv build`](../reference/cli/thv_build.md) command. These +patterns include multi-architecture builds, supply chain security, and efficient +change detection. + +These patterns are intended for anyone building MCP server containers regularly, +such as maintainers of MCP servers or organizations deploying custom servers or +repackaging existing ones. + +## Prerequisites + +Before implementing these advanced patterns, ensure you have: + +- Basic understanding of [`thv build`](./build-containers.mdx) command +- Experience with CI/CD pipelines (GitHub Actions, GitLab CI, etc.) +- Container registry access for pushing images +- Understanding of Docker Buildx for multi-architecture builds + +## Multi-architecture MCP server builds + +Build MCP server containers for multiple architectures (amd64, arm64) using +Docker Buildx: + +```yaml +name: Multi-arch Build +on: + push: + tags: ['v*'] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Install ToolHive + uses: StacklokLabs/toolhive-actions/install@v0 + with: + version: latest + + - name: Generate Dockerfile + run: | + thv build --dry-run --output Dockerfile uvx://mcp-server-git + + - name: Build multi-arch container + run: | + docker buildx build \ + --platform linux/amd64,linux/arm64 \ + --tag ghcr.io/myorg/mcp-server:${{ github.ref_name }} \ + --push \ + . +``` + +## Supply chain security + +Enhance security with SBOM generation, provenance attestation, and image +signing: + +```yaml +name: Secure Build +on: + push: + tags: ['v*'] + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + id-token: write + steps: + - uses: actions/checkout@v4 + + - name: Install Cosign + uses: sigstore/cosign-installer@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Install ToolHive + uses: StacklokLabs/toolhive-actions/install@v0 + with: + version: latest + + - name: Generate Dockerfile + run: | + thv build --dry-run --output Dockerfile uvx://mcp-server-git + + - name: Build with security features + uses: docker/build-push-action@v6 + id: build + with: + context: . + platforms: linux/amd64,linux/arm64 + push: true + tags: ghcr.io/myorg/mcp-server:${{ github.ref_name }} + sbom: true # Generate Software Bill of Materials + provenance: true # Generate build provenance + cache-from: type=gha + cache-to: type=gha,mode=max + + - name: Sign container image + env: + DIGEST: ${{ steps.build.outputs.digest }} + run: | + cosign sign --yes ghcr.io/myorg/mcp-server@${DIGEST} +``` + +## Efficient change detection + +Build only when relevant files change to optimize CI/CD performance: + +```yaml +name: Conditional Build +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + detect-changes: + runs-on: ubuntu-latest + outputs: + build_needed: ${{ steps.changes.outputs.build_needed }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 2 + + - name: Detect changes + id: changes + run: | + # Check if MCP server configs or Dockerfiles changed + if git diff --name-only HEAD~1..HEAD | grep -E "(mcp-configs/|Dockerfile|\.thv)"; then + echo "build_needed=true" >> $GITHUB_OUTPUT + else + echo "build_needed=false" >> $GITHUB_OUTPUT + fi + + build: + needs: detect-changes + if: needs.detect-changes.outputs.build_needed == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install ToolHive + uses: StacklokLabs/toolhive-actions/install@v0 + with: + version: latest + + - name: Build containers + run: | + thv build --tag ghcr.io/myorg/mcp-server:latest uvx://mcp-server-git +``` + +## Matrix builds for multiple servers + +Build multiple MCP servers in parallel using matrix strategies: + +```yaml +name: Matrix Build +on: + push: + tags: ['v*'] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + server: + - name: git-server + scheme: uvx://mcp-server-git + - name: filesystem-server + scheme: npx://@modelcontextprotocol/server-filesystem + - name: custom-server + scheme: go://github.com/myorg/custom-mcp-server@latest + steps: + - uses: actions/checkout@v4 + + - name: Install ToolHive + uses: StacklokLabs/toolhive-actions/install@v0 + with: + version: latest + + - name: Build ${{ matrix.server.name }} + run: | + thv build --tag ghcr.io/myorg/${{ matrix.server.name }}:${{ github.ref_name }} \ + ${{ matrix.server.scheme }} + + - name: Push ${{ matrix.server.name }} + run: | + docker push ghcr.io/myorg/${{ matrix.server.name }}:${{ github.ref_name }} +``` + +## Vulnerability scanning + +Integrate security scanning into your build pipeline: + +```yaml +name: Secure Build with Scanning +on: + push: + tags: ['v*'] + +jobs: + build-and-scan: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install ToolHive + uses: StacklokLabs/toolhive-actions/install@v0 + with: + version: latest + + - name: Build container + run: | + thv build --tag mcp-server:scan uvx://mcp-server-git + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@master + with: + image-ref: 'mcp-server:scan' + format: 'sarif' + output: 'trivy-results.sarif' + + - name: Upload Trivy scan results + uses: github/codeql-action/upload-sarif@v3 + if: always() + with: + sarif_file: 'trivy-results.sarif' + + - name: Tag and push if scan passes + run: | + docker tag mcp-server:scan ghcr.io/myorg/mcp-server:${{ github.ref_name }} + docker push ghcr.io/myorg/mcp-server:${{ github.ref_name }} +``` + +## GitLab CI example + +For GitLab CI users, here's an equivalent pipeline: + +```yaml +# .gitlab-ci.yml +stages: + - build + - security + - deploy + +variables: + DOCKER_DRIVER: overlay2 + DOCKER_TLS_CERTDIR: '/certs' + +build: + stage: build + image: docker:latest + services: + - docker:dind + before_script: + # Install ToolHive CLI using curl and jq to get the latest version + - | + VERSION=$(curl -s https://api.github.com/repos/stacklok/toolhive/releases/latest | jq -r .tag_name) + wget "https://github.com/stacklok/toolhive/releases/download/${VERSION}/toolhive_${VERSION#v}_linux_amd64.tar.gz" + tar -xzf "toolhive_${VERSION#v}_linux_amd64.tar.gz" + install -m 0755 thv /usr/local/bin/ + - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY + script: + - thv build --tag $CI_REGISTRY_IMAGE/mcp-server:$CI_COMMIT_TAG + uvx://mcp-server-git + - docker push $CI_REGISTRY_IMAGE/mcp-server:$CI_COMMIT_TAG + only: + - tags + +security_scan: + stage: security + image: aquasec/trivy:latest + script: + - trivy image --exit-code 1 --severity HIGH,CRITICAL + $CI_REGISTRY_IMAGE/mcp-server:$CI_COMMIT_TAG + only: + - tags +``` + +## Best practices + +When implementing advanced CI/CD patterns: + +1. **Use specific tags** instead of `latest` for production deployments +2. **Implement proper caching** to speed up builds +3. **Scan for vulnerabilities** before pushing to production registries +4. **Sign images** for supply chain security +5. **Use matrix builds** for multiple MCP servers +6. **Implement change detection** to avoid unnecessary builds +7. **Store sensitive data** in CI/CD secrets, not in code + +## Next steps + +- [Deploy to Kubernetes](../guides-k8s/run-mcp-k8s.mdx) to run your CI/CD-built + servers in production +- [Aggregate multiple servers](../guides-vmcp/index.mdx) into a single endpoint + with Virtual MCP Server + +## Related information + +- [Build MCP server containers](./build-containers.mdx) +- [`thv build` command reference](../reference/cli/thv_build.md) +- [Secrets management](./secrets-management.mdx) diff --git a/versioned_docs/version-1.1/toolhive/guides-cli/api-server.mdx b/versioned_docs/version-1.1/toolhive/guides-cli/api-server.mdx new file mode 100644 index 00000000..458acf6c --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-cli/api-server.mdx @@ -0,0 +1,95 @@ +--- +title: Run the API server +description: How to run the local ToolHive API server. +--- + +ToolHive includes a built-in API server that provides a RESTful interface for +interacting with MCP servers. The API server is useful for integrating ToolHive +with other applications or automating tasks. + +:::note + +The API server isn't intended for production use. It's designed for local +automation and UI development, and doesn't implement any authentication or +authorization mechanisms. + +For production use cases, consider using the ToolHive Kubernetes Operator, which +provides a more robust and secure way to manage ToolHive instances in a +multi-user environment. + +::: + +## Start the API server + +To start the API server, use the following command: + +```bash +thv serve +``` + +This starts the API server on `localhost` (127.0.0.1) using the default port +`8080`. + +Test the API server using `curl` or a web browser: + +```bash +curl http://localhost:8080/api/v1beta/status +``` + +You should see a JSON response with the current ToolHive version. + +## Custom networking + +By default, the API server listens on `localhost` (127.0.0.1) port `8080`. + +You can specify a different port using the `--port` option: + +```bash +thv serve --port +``` + +If you're running the API server on a remote host, specify the hostname or IP +address to bind to using the `--host` option: + +```bash +thv serve --host +``` + +## UNIX socket support + +The API server can also be exposed via a UNIX socket instead of a TCP port. Use +the `--socket` option to specify a socket path: + +```bash +thv serve --socket /tmp/toolhive.sock +``` + +When using a UNIX socket, the `--socket` argument overrides the host:port +address configuration. + +## API documentation + +See the [ToolHive API documentation](../reference/api.mdx) for details on +available endpoints, request and response formats. + +You can also run a local instance of the API documentation using the `--openapi` +option: + +```bash +thv serve --openapi +``` + +Open a browser to `http://localhost:8080/api/doc` to view the API documentation. +The OpenAPI specification is also available at +`http://localhost:8080/api/openapi.json`. + +## Next steps + +- [Secure your servers](./auth.mdx) with authentication and authorization +- [Monitor server activity](./telemetry-and-metrics.mdx) with OpenTelemetry and + Prometheus + +## Related information + +- [ToolHive API documentation](../reference/api.mdx) +- [`thv serve` command reference](../reference/cli/thv_serve.md) diff --git a/versioned_docs/version-1.1/toolhive/guides-cli/auth.mdx b/versioned_docs/version-1.1/toolhive/guides-cli/auth.mdx new file mode 100644 index 00000000..b1517c15 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-cli/auth.mdx @@ -0,0 +1,165 @@ +--- +title: Authentication and authorization +description: + How to set up authentication and authorization for MCP servers using the + ToolHive CLI. +--- + +import OidcPrerequisites from '../_partials/_oidc-prerequisites.mdx'; +import BasicCedarConfig from '../_partials/_basic-cedar-config.mdx'; +import AuthTroubleshooting from '../_partials/_auth-troubleshooting.mdx'; + +This guide shows you how to secure your MCP servers using OAuth-based +authentication and Cedar-based authorization policies with the ToolHive CLI. + +:::info + +Authentication and authorization are emerging capabilities in the MCP ecosystem. +The official MCP authorization specification is still evolving, and client +support for these features is limited. ToolHive is leading the way in +implementing these capabilities, but you may encounter some limitations with +certain clients. + +::: + +## Prerequisites + + + +## Set up authentication + +### Step 1: Gather OIDC configuration + +First, collect the necessary information from your identity provider: + +- Client ID +- Audience value +- Issuer URL +- JWKS URL (for key verification) + +### Step 2: Run an MCP server with authentication + +Use the following command to start an MCP server with authentication enabled: + +```bash +thv run \ + --oidc-audience \ + --oidc-client-id \ + --oidc-issuer \ + --oidc-jwks-url \ + +``` + +Replace the placeholders with your actual OIDC configuration. + +### Step 3: Test authentication + +Once your server is running with authentication enabled, clients must include a +valid JWT (JSON Web Token) in the `Authorization` header of each HTTP request. +The token should: + +- Be issued by your configured identity provider +- Include the correct audience claim +- Not be expired +- Have a valid signature + +:::note[Client support limitations] + +Client support for authentication is limited at this time. While some clients +support HTTP headers with SSE MCP client configurations, you should not pass JWT +tokens in this way since it requires manual configuration and exposes your token +in plain text. + +ToolHive is working on a solution to securely handle authentication for clients. +Stay tuned for updates on this feature. + +::: + +:::note[Obtaining JWT tokens] + +How to obtain JWT tokens varies by identity provider and is outside the scope of +this guide. Consult your identity provider's documentation for specific +instructions on: + +- Interactive user authentication flows (OAuth 2.0 Authorization Code flow) +- Service-to-service authentication (Client Credentials flow) +- API token generation and management + +For Kubernetes service accounts, tokens are automatically mounted at +`/var/run/secrets/kubernetes.io/serviceaccount/token` in pods. + +::: + +To verify that authentication is working, you can use a tool like `curl` to make +a request to your MCP server: + +```bash +curl -H "Authorization: Bearer " \ + +``` + +## Set up authorization + +ToolHive uses Amazon's Cedar policy language for fine-grained, secure-by-default +authorization. Authorization is explicit: if a request is not explicitly +permitted by a policy, it is denied. Deny rules always take precedence over +permit rules. + +### Step 1: Create an authorization configuration file + + + +Save this file to a location accessible to ToolHive, such as +`/path/to/authz-config.json`. + +### Step 2: Run an MCP server with authorization + +Start your MCP server with the authorization configuration: + +```bash +thv run \ + --authz-config /path/to/authz-config.json \ + +``` + +You can combine this with the authentication parameters from the previous +section: + +```bash +thv run \ + --oidc-audience \ + --oidc-client-id \ + --oidc-issuer \ + --oidc-jwks-url \ + --authz-config /path/to/authz-config.json \ + +``` + +### Step 3: Test authorization + +Once your server is running with authorization enabled, clients will be subject +to the Cedar policies defined in your configuration file. When a client attempts +to perform an action, ToolHive will evaluate the request against the policies. +If the request is permitted, the action will proceed; otherwise, it will be +denied with a 403 Forbidden response. + +## Next steps + +- [Configure token exchange](./token-exchange.mdx) to let MCP servers + authenticate to backend services +- [Enable telemetry and metrics](./telemetry-and-metrics.mdx) to gain + observability into MCP server interactions +- [Learn about the auth framework](../concepts/auth-framework.mdx) for a deeper + understanding of ToolHive's authentication and authorization model + +## Related information + +- [Authentication and authorization framework](../concepts/auth-framework.mdx) + for conceptual understanding +- [Cedar policies](../concepts/cedar-policies.mdx) and the + [Cedar documentation](https://docs.cedarpolicy.com/) for detailed policy + syntax + +## Troubleshooting + + diff --git a/versioned_docs/version-1.1/toolhive/guides-cli/build-containers.mdx b/versioned_docs/version-1.1/toolhive/guides-cli/build-containers.mdx new file mode 100644 index 00000000..fae9a1a4 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-cli/build-containers.mdx @@ -0,0 +1,501 @@ +--- +title: Build MCP containers +description: + How to build MCP server containers without running them using the ToolHive + CLI. +--- + +This guide explains how to use the [`thv build`](../reference/cli/thv_build.md) +command to build MCP server containers from protocol schemes without running +them. This is useful for pre-building containers for Kubernetes deployments, +CI/CD pipelines, and container registry workflows. + +## Overview + +The `thv build` command allows you to build containers from protocol schemes +(`uvx://`, `npx://`, `go://`) without immediately running them. This provides +several benefits: + +- **Pre-build containers** for faster deployment in Kubernetes environments +- **Separate build and run phases** in CI/CD pipelines +- **Custom image tagging** for container registry workflows +- **Dockerfile generation** for inspection and customization +- **Build validation** before deployment + +## Basic usage + +To build a container from a protocol scheme: + +```bash +thv build +``` + +For example: + +```bash +# Build a Python MCP server using uvx +thv build uvx://mcp-server-git + +# Build a Node.js MCP server using npx with a pinned version +thv build npx://@upstash/context7-mcp@1.0.26 + +# Build a Go MCP server +thv build go://github.com/example/my-mcp-server@latest +``` + +:::info[What's happening?] + +When you run `thv build`, ToolHive: + +1. Detects the protocol scheme and extracts the package reference +2. Generates a Dockerfile based on the appropriate template +3. Builds a Docker image with the package installed +4. Tags the image with an auto-generated name or your custom tag +5. Displays the built image name for use with other tools + +::: + +## Custom image tagging + +Use the `--tag` (or `-t`) flag to specify a custom name and tag for the built +image: + +```bash +thv build --tag my-custom-name:latest npx://@modelcontextprotocol/server-filesystem +``` + +This is particularly useful for: + +- **Container registries**: Tag images for pushing to registries +- **Kubernetes deployments**: Use predictable image names in manifests +- **Version management**: Tag images with specific versions + +### Tagging examples + + + + +Build and tag for pushing to a container registry: + +```bash +# Build and tag for Docker Hub +thv build --tag myusername/mcp-git-server:v1.0.0 uvx://mcp-server-git + +# Build and tag for GitHub Container Registry +thv build --tag ghcr.io/myorg/mcp-filesystem:latest npx://@modelcontextprotocol/server-filesystem + +# Push to registry +docker push ghcr.io/myorg/mcp-filesystem:latest +``` + + + + +Build images with predictable names for Kubernetes manifests: + +```bash +# Build with a consistent tag +thv build --tag mcp-servers/git-server:stable uvx://mcp-server-git +``` + +Use the built image in your Kubernetes manifests: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: git-server + namespace: production +spec: + image: mcp-servers/git-server:stable + # ... other spec fields ... +``` + + + + +Build multiple versions of the same server: + +```bash +# Build different versions +thv build --tag mcp-git:v1.0.0 uvx://mcp-server-git@1.0.0 +thv build --tag mcp-git:v1.1.0 uvx://mcp-server-git@1.1.0 +thv build --tag mcp-git:latest uvx://mcp-server-git@latest +``` + + + + +## Protocol schemes + +The `thv build` command supports the same protocol schemes as +[`thv run`](./run-mcp-servers.mdx#run-a-server-using-protocol-schemes): + +### Python (uvx) + +Build Python-based MCP servers using the uv package manager: + +```bash +# Build with auto-generated name +thv build uvx://mcp-server-git + +# Build with custom tag +thv build --tag my-git-server:latest uvx://mcp-server-git@1.2.0 +``` + +### Node.js (npx) + +Build Node.js-based MCP servers using npm: + +```bash +# Build with auto-generated name +thv build npx://@modelcontextprotocol/server-filesystem + +# Build with custom tag +thv build --tag filesystem-server:v2.0 npx://@modelcontextprotocol/server-filesystem@2.0.0 +``` + +### Go + +Build Go-based MCP servers: + +```bash +# Build from remote Go module +thv build --tag grafana-mcp:latest go://github.com/grafana/mcp-grafana/cmd/mcp-grafana@latest + +# Build from local Go project +thv build --tag my-local-server:dev go://./cmd/my-mcp-server +``` + +## Build-time arguments + +Some MCP servers require specific subcommands or arguments that must always be +present. You can bake these required arguments directly into the container image +at build time using the `--` separator at the end of the `thv build` command: + +```bash +thv build -- +``` + +:::info[Build-time vs runtime arguments] + +- **Build-time arguments**: Baked into the container image and always present. + These are typically required subcommands or essential configuration flags. +- **Runtime arguments**: Passed when running the container and appended after + build-time arguments. These are typically optional flags or dynamic + configuration. + +::: + +Build-time arguments are embedded in the container's ENTRYPOINT and always +execute before any runtime arguments. For example, the LaunchDarkly MCP server +requires a `start` subcommand: + +```bash +# Bake "start" subcommand into container +thv build --tag launchdarkly-mcp:latest npx://@launchdarkly/mcp-server -- start + +# Runtime args still append after baked-in args +thv run launchdarkly-mcp:latest -- --verbose +# Executes: npx @launchdarkly/mcp-server start --verbose +``` + +You can include multiple build-time arguments as needed: + +```bash +thv build uvx://my-package -- --transport stdio --log-level info +``` + +## Dockerfile generation + +Use the `--dry-run` flag to generate the Dockerfile without building the image: + +```bash +# Output Dockerfile to stdout +thv build --dry-run uvx://mcp-server-git + +# Save Dockerfile to a file +thv build --dry-run --output Dockerfile.mcp-git uvx://mcp-server-git +``` + +This is useful for: + +- **Inspecting the build process** before building +- **Customizing Dockerfiles** for specific requirements +- **Understanding dependencies** and build steps +- **Debugging build issues** + +### Example Dockerfile output + +```dockerfile +# Generated by: thv build --dry-run uvx://mcp-server-git +FROM python:3.12-slim + +# Install uv +RUN pip install uv + +# Install the package +RUN uv tool install mcp-server-git + +# Set the entrypoint +ENTRYPOINT ["uv", "tool", "run", "mcp-server-git"] +``` + +## Kubernetes workflows + +The `thv build` command is especially useful for Kubernetes deployments where +you need to pre-build containers before deploying them. + +### Pre-build workflow + +1. **Build the container** with a specific tag: + + ```bash + thv build --tag ghcr.io/myorg/mcp-git:v1.0.0 uvx://mcp-server-git@1.0.0 + ``` + +2. **Push to container registry**: + + ```bash + docker push ghcr.io/myorg/mcp-git:v1.0.0 + ``` + +3. **Deploy to Kubernetes** using the pre-built image: + + ```yaml + apiVersion: toolhive.stacklok.dev/v1alpha1 + kind: MCPServer + metadata: + name: git-server + namespace: production + spec: + image: ghcr.io/myorg/mcp-git:v1.0.0 + transport: stdio + ``` + +### CI/CD integration + +Integrate `thv build` into your CI/CD pipeline for automated container building: + +```yaml +# Example GitHub Actions workflow +name: Build and Deploy MCP Server +on: + push: + tags: ['v*'] + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install ToolHive + uses: StacklokLabs/toolhive-actions/install@v0 + with: + version: latest + + - name: Log in to Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Build MCP server + run: | + thv build --tag ghcr.io/${{ github.repository }}/mcp-server:${{ github.ref_name }} \ + uvx://mcp-server-git@${{ github.ref_name }} + + - name: Push to registry + run: | + docker push ghcr.io/${{ github.repository }}/mcp-server:${{ github.ref_name }} +``` + +For more advanced CI/CD patterns including multi-architecture builds, supply +chain security, and change detection, see the +[Advanced CI/CD patterns](./advanced-cicd.mdx) guide. + +## Advanced usage + +### Build with custom CA certificates + +For corporate environments with custom certificate authorities: + +```bash +# Use global CA certificate configuration +thv config set-ca-cert /path/to/corporate-ca.crt +thv build uvx://internal-mcp-server + +# Override CA certificate for specific build +thv build --ca-cert /path/to/special-ca.crt uvx://special-server +``` + +### Custom package registries + +Enterprise environments often use private package registries or mirrors instead +of public registries like npm or PyPI. ToolHive supports configuring build +environment variables that are injected into the Dockerfile during builds, +allowing you to use custom registries for all protocol scheme builds. + +#### Set build environment variables + +Use the `thv config set-build-env` command to configure environment variables +that will be included in all builds: + +```bash +thv config set-build-env +``` + +Common environment variables for package registries: + +| Package manager | Environment variable | Example value | +| --------------- | --------------------- | -------------------------------------- | +| npm | `NPM_CONFIG_REGISTRY` | `https://npm.corp.example.com` | +| Go | `GOPROXY` | `https://goproxy.corp.example.com` | +| Go | `GOPRIVATE` | `github.com/mycompany/*` | +| pip/uv | `PIP_INDEX_URL` | `https://pypi.corp.example.com/simple` | +| pip/uv | `PIP_TRUSTED_HOST` | `pypi.corp.example.com` | + +Example configuration for an enterprise environment: + +```bash +# Configure npm to use a corporate registry +thv config set-build-env NPM_CONFIG_REGISTRY https://npm.corp.example.com + +# Configure Go proxy for private modules +thv config set-build-env GOPROXY https://goproxy.corp.example.com +thv config set-build-env GOPRIVATE "github.com/mycompany/*" + +# Configure Python/uv to use a corporate PyPI mirror +thv config set-build-env PIP_INDEX_URL https://pypi.corp.example.com/simple +``` + +### Build local Go projects + +Build MCP servers from local Go projects: + +```bash +# Build from current directory +cd my-go-mcp-project +thv build --tag my-server:dev go://. + +# Build from relative path +thv build --tag my-server:dev go://./cmd/server + +# Build from absolute path +thv build --tag my-server:dev go:///path/to/my-project +``` + +## Comparison with thv run + +| Feature | `thv build` | `thv run` | +| ------------------------- | ------------------------ | ------------------------ | +| **Purpose** | Build containers only | Build and run containers | +| **Output** | Container image | Running MCP server | +| **Use case** | Pre-building, CI/CD | Development, testing | +| **Kubernetes** | Pre-build for deployment | Direct development | +| **Custom tagging** | ✅ `--tag` flag | ❌ Auto-generated names | +| **Dockerfile generation** | ✅ `--dry-run` flag | ❌ Not available | + +## Next steps + +- Use built containers with [`thv run`](./run-mcp-servers.mdx) for local + development +- Deploy pre-built containers to [Kubernetes](../guides-k8s/run-mcp-k8s.mdx) +- Set up [CI/CD pipelines](#cicd-integration) for automated building +- Learn about [container registry workflows](#custom-image-tagging) + +## Related information + +- [`thv build` command reference](../reference/cli/thv_build.md) +- [Run MCP servers](./run-mcp-servers.mdx) +- [Run MCP servers in Kubernetes](../guides-k8s/run-mcp-k8s.mdx) +- [Custom permissions](./custom-permissions.mdx) + +## Troubleshooting + +
+Build fails with network errors + +If builds fail with network connectivity issues: + +1. **Check internet connectivity** for downloading packages +2. **Configure CA certificates** for corporate environments: + + ```bash + thv config set-ca-cert /path/to/corporate-ca.crt + ``` + +3. **Use proxy settings** if required by your network +4. **Verify package names** and versions exist in the respective registries + +
+ +
+Invalid image tag format + +If you get image tag validation errors: + +1. **Use valid Docker image tag format**: `name:tag` or `registry/name:tag` +2. **Avoid special characters** except hyphens, underscores, and dots +3. **Use lowercase names** for compatibility +4. **Check tag length limits** (typically 128 characters) + +Example valid tags: + +```bash +thv build --tag my-server:latest uvx://package +thv build --tag ghcr.io/org/server:v1.0.0 npx://package +``` + +
+ +
+Package not found errors + +If the build fails because a package cannot be found: + +1. **Verify package exists** in the respective registry: + - Python: Check [PyPI](https://pypi.org/) + - Node.js: Check [npm](https://www.npmjs.com/) + - Go: Verify the module path and version + +2. **Check version specifiers**: + + ```bash + # Correct version formats + thv build uvx://package@1.0.0 + thv build npx://package@latest + thv build go://github.com/user/repo@v1.0.0 + ``` + +3. **For Go modules**, ensure the path includes the correct import path + +
+ +
+Custom registry not being used + +If your custom package registry configuration isn't being applied: + +1. **Verify your build environment configuration**: + + ```bash + thv config get-build-env + ``` + +2. **Check the environment variable names** match what your package manager + expects (for example, `NPM_CONFIG_REGISTRY` for npm, `GOPROXY` for Go) + +3. **Use `--dry-run` to inspect the generated Dockerfile** and verify your + environment variables are being injected: + + ```bash + thv build --dry-run uvx://my-package + ``` + +4. **Ensure network connectivity** to your custom registry from inside the + container build environment + +
diff --git a/versioned_docs/version-1.1/toolhive/guides-cli/client-configuration.mdx b/versioned_docs/version-1.1/toolhive/guides-cli/client-configuration.mdx new file mode 100644 index 00000000..88f5dd47 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-cli/client-configuration.mdx @@ -0,0 +1,287 @@ +--- +title: Client configuration +description: How to configure AI agent clients to work with ToolHive MCP servers. +--- + +import ClientIntro from '../_partials/_client-config-intro.mdx'; + + + +## Register clients + +The easiest way to register clients is to run the +[setup command](../reference/cli/thv_client_setup.md): + +```bash +thv client setup +``` + +ToolHive discovers supported clients installed on your system and lets you +select which ones to register. ToolHive detects clients based on the presence of +the client's configuration file in its default location. See the +[Client compatibility reference](../reference/client-compatibility.mdx) for +details on which clients ToolHive supports and how it detects them. + +To view the current status of detected and configured clients, run: + +```bash +thv client status +``` + +:::note + +ToolHive previously included an "auto-discovery" mode. We removed this mode in +v0.1.0 to simplify client configuration and ensure consistent control and +behavior. If you previously enabled client auto-discovery, ToolHive will +explicitly register all detected clients the first time you run v0.1.0 and +higher. + +Going forward, use the `thv client setup` command to manage your client +configurations. + +::: + +## Alternative client registration + +If you prefer to register clients manually or in an automated script, use the +`thv client register` command: + +```bash +thv client register +``` + +Replace `` with the name of your client. Common client names +include: + +- `claude-code` - Claude Code CLI +- `cursor` - Cursor IDE +- `roo-code` - Roo Code extension for Visual Studio Code +- `cline` - Cline extension for Visual Studio Code +- `vscode` - Visual Studio Code (GitHub Copilot) +- `vscode-insider` - VS Code Insiders edition + +Example: + +```bash +thv client register vscode +``` + +You can register a client with a specific group using the `--group` option: + +```bash +thv client register --group +``` + +Run [`thv client register --help`](../reference/cli/thv_client_register.md) for +the latest list of supported clients. + +To list currently registered clients: + +```bash +thv client list-registered +``` + +Repeat the registration step for any additional clients you want to configure. + +You might need to restart your client application for the configuration to take +effect. + +To remove a client configuration: + +```bash +thv client remove +``` + +To remove a client configuration from a specific group: + +```bash +thv client remove --group +``` + +## Other clients or tools + +If you have other clients or development libraries that ToolHive doesn't +directly support, you can still configure them to use ToolHive-managed MCP +servers if they support the SSE or Streamable HTTP protocol. Check your client +or library documentation for configuration details. + +List your running MCP servers to get the URL: + +```bash +thv list +``` + +Example output (some fields omitted for brevity): + +```text +NAME PACKAGE STATUS URL PORT +github ghcr.io/github/github-mcp-server:latest running http://127.0.0.1:55264/mcp 55264 +sqlite ghcr.io/stackloklabs/sqlite-mcp/server:latest running http://127.0.0.1:22089/sse#sqlite 22089 +``` + +In this example, the `github` server uses the Streamable HTTP transport (URL +ends in `/mcp`), and the `sqlite` server uses SSE (URL ends in `/sse`). + +You can also get the list in JSON format, which works with many clients that use +the standard configuration format: + +```bash +thv list --format mcpservers +``` + +Example output: + +```json +{ + "mcpServers": { + "github": { + "url": "http://127.0.0.1:55264/mcp" + }, + "sqlite": { + "url": "http://127.0.0.1:22089/sse#sqlite" + } + } +} +``` + +Configure your client or library to connect to the MCP server using the URL +ToolHive provides. + +## Next steps + +- [Manage agent skills](./skills-management.mdx) to install reusable skills for + your AI clients +- [Set up custom permissions](./custom-permissions.mdx) to control filesystem + and network access for your servers +- [Secure your servers](./auth.mdx) with OIDC authentication and Cedar policies + +## Related information + +- [`thv client` command reference](../reference/cli/thv_client.md) +- [`thv config` command reference](../reference/cli/thv_config.md) +- [Client compatibility](../reference/client-compatibility.mdx) +- [Run MCP servers](./run-mcp-servers.mdx) + +## Troubleshooting + +
+Client is not detected by `thv client setup` + +If ToolHive doesn't detect your client: + +1. Verify ToolHive supports your client in the + [Client compatibility reference](../reference/client-compatibility.mdx). + +2. Make sure you installed the client in its default location. ToolHive detects + clients based on their configuration files. If the client isn't in its + default location, ToolHive can't detect it. + +3. Try manually registering the client: + + ```bash + thv client register + ``` + +
+ +
+Client can't connect to MCP server + +If your client can't connect to the MCP server: + +1. Verify the MCP server is running: + + ```bash + thv list + ``` + + See [Test MCP servers](./test-mcp-servers.mdx) for help testing the MCP + server's availability. + +2. Check if the client is registered: + + ```bash + thv client status + ``` + +3. Restart your client application. + +
+ +
+Client can connect but tools aren't available + +If your client connects to the MCP server but tools aren't available: + +1. Make sure the MCP server is running and accessible: + + ```bash + thv list + ``` + + See [Test MCP servers](./test-mcp-servers.mdx) for help testing the MCP + server's functionality. + +2. Check the MCP server logs: + + ```bash + thv logs + ``` + +3. Make sure you properly configured the MCP server in your client. +4. If the MCP server requires authentication or has authorization policies + applied, make sure the client has the necessary permissions to access the + tools. + +
+ +
+Containerized client can't connect to MCP server + +When your MCP client runs in a container (like containerized LibreChat), it +can't reach the ToolHive proxy on your host machine using `localhost` due to +container network isolation. The ToolHive proxy runs as an OS process on the +host and provides access to containerized MCP servers. + +Configure your containerized client to use the appropriate host address for your +platform: + +- **Docker Desktop (macOS/Windows)**: `host.docker.internal` +- **Podman Desktop**: `host.containers.internal` +- **Docker Engine (Linux)**: `172.17.0.1` (or your custom bridge gateway IP) + +For example, change the MCP server URL from `http://localhost:8080/mcp` to +`http://host.docker.internal:8080/mcp` in your client configuration. + +
+ +
+VS Code can't connect to some streamable-http servers + +You might encounter errors with Visual Studio Code connecting to some +Python-based MCP servers using the Streamable HTTP transport protocol: + +```text +[info] Connection state: Error Error sending message to http://localhost:49574/mcp: TypeError: fetch failed +[error] Server exited before responding to `initialize` request. +``` + +This is a known interaction between VS Code and certain versions of the FastMCP +SDK used by Python-based MCP servers. If you inspect the HTTP connection, you'll +see a `307 Temporary Redirect` response, which VS Code doesn't handle correctly. + +This +[issue is resolved](https://github.com/modelcontextprotocol/python-sdk/pull/781) +in the latest versions of the SDK, but if you're using an older version of a +Python-based MCP server, you might still encounter it. + +There are two workarounds: + +1. Change the URL in your VS Code settings to add a trailing slash to the MCP + server URL. For example, change `http://localhost:49574/mcp` to + `http://localhost:49574/mcp/`. You'll need to re-apply this if you stop and + restart the MCP server. +2. If the MCP server supports SSE, switch to using the SSE transport instead of + Streamable HTTP. + +
diff --git a/versioned_docs/version-1.1/toolhive/guides-cli/custom-permissions.mdx b/versioned_docs/version-1.1/toolhive/guides-cli/custom-permissions.mdx new file mode 100644 index 00000000..cdad21dd --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-cli/custom-permissions.mdx @@ -0,0 +1,242 @@ +--- +title: Custom permissions +description: + How to create and apply file system permissions and network isolation for MCP + servers using permission profiles in ToolHive. +--- + +ToolHive includes a permission system that lets you control an MCP server's +access to your host's file system and network resources. This helps you maintain +security and ensures that MCP servers operate within defined boundaries. + +## Understanding permission profiles + +Permissions are defined using _permission profiles_—JSON files that specify both +file system and network access rules for an MCP server. Each MCP server can use +only one permission profile at a time, so you include all necessary permissions +in a single profile. + +Permission profiles control two types of access: + +- **Host file system access** specifies paths on your host that are mounted into + the MCP server container — see [file system access](./filesystem-access.mdx) + for detailed examples +- **Network access rules** let you restrict outbound HTTP(S) connectivity from + the MCP server — see [network isolation](./network-isolation.mdx) for + architecture details and examples + +### Profile structure + +Profiles are defined in JSON format and can include the following properties: + +- `read`: List of paths on your host file system that the MCP server can read +- `write`: List of file system paths that the MCP server can write to (this also + implies read access) +- `network`: Network access rules for inbound and outbound connections: + - `inbound`: Inbound network access rules, which include: + - `allow_host`: List of allowed hostnames that can send traffic to the MCP + server. If not specified, only the container's own hostname, `localhost`, + and `127.0.0.1` are allowed. + - `outbound`: Outbound network access rules, which include: + - `insecure_allow_all`: If set to `true`, allows unrestricted outbound + network access. This isn't recommended for production use. + - `allow_host`: List of allowed hostnames or IP addresses for outbound + connections. To allow all subdomains of a domain, prefix the domain with a + period (e.g., `.github.com` allows any subdomain of `github.com`). + Wildcards are not supported. + - `allow_port`: List of allowed ports for outbound connections + +## Default permissions in the ToolHive registry + +ToolHive includes default least-privilege permissions for MCP servers in the +built-in registry. These defaults balance functionality and security, but you +should review them to make sure they meet your specific requirements. + +View these permissions using the following command: + +```bash +thv registry info +``` + +In the output, look for the "Permissions" section: + +```test +Permissions: + Network: + Allow Host: .google.com + Allow Port: 443 +``` + +This example shows that the MCP server can make outbound network connections to +`*.google.com` (note the leading `.` which enables subdomain matching) on +port 443. + +:::info + +For security reasons, none of the MCP servers in the registry have any default +file system permissions defined. This means they cannot read or write to any +paths on your host system unless you explicitly grant access using a custom +permission profile or use the `--volume` flag when running the server. + +::: + +Always verify the default permissions and override them with a custom profile if +needed to meet your security policies. + +:::tip + +Add `--format json` to the +[`thv registry info`](../reference/cli/thv_registry_info.md) command to get the +output in JSON format for easier customization. Use the contents of the +`permissions` section as a starting point for creating a custom profile. + +::: + +## Built-in profiles + +ToolHive includes two built-in profiles for network access that you can use +without creating a custom file: + +- **`network`**: Permits all outbound network access. This is the default + profile applied to MCP servers when you run a custom server without the + `--permission-profile` flag. + + :::info[Important] + + This profile is useful for development and testing but isn't recommended for + production use since it doesn't restrict network destinations. Create a custom + profile that specifies the allowed hosts and ports when possible. + + ::: + +- **`none`**: Restricts all network access. Use this for MCP servers that don't + require external connectivity. + +Both built-in profiles provide no file system access by default. To add file +system permissions, either: + +- Use the `--volume` flag to mount specific paths +- Create a custom profile that includes both network settings and file system + permissions + +## Create a custom permission profile + +You can create a JSON file with your desired permissions. Include file system +permissions, network permissions, or both in the same profile. + +### Example: Combined file system and network permissions + +When your MCP server needs both file access and network connectivity, include +both types of permissions in a single profile: + +```json title="~/my-server-profile.json" +{ + "read": ["/home/user/documents", "/home/user/config"], + "write": ["/home/user/output"], + "network": { + "outbound": { + "insecure_allow_all": false, + "allow_host": ["api.github.com", ".googleapis.com"], + "allow_port": [443] + } + } +} +``` + +This profile: + +- Mounts `/home/user/documents` and `/home/user/config` as read-only paths +- Mounts `/home/user/output` with read and write access (note that `write` also + implies read access) +- Allows outbound HTTPS connections to `api.github.com` and any subdomain of + `googleapis.com` + +### Example: Network-only permissions + +Use this approach when your MCP server needs to make API calls but doesn't +require file system access: + +```json title="~/network-only-profile.json" +{ + "network": { + "outbound": { + "allow_host": ["host.docker.internal", ".intranet.example.com"], + "allow_port": [8080, 3000] + } + } +} +``` + +This profile allows the server to connect to local development servers and +internal company resources without granting any file system access. + +See [network isolation](./network-isolation.mdx) for more details about network +permissions and how isolation works. + +### Example: File system-only permissions + +Use this when your MCP server works with local files but doesn't need network +access: + +```json title="~/filesystem-only-profile.json" +{ + "read": ["/var/log"], + "write": ["/tmp/mcp-output"] +} +``` + +This profile grants file system access without defining any network permissions. +(Note, to actually block network access, use the `--isolate-network` flag when +running the server.) + +See [file system access](./filesystem-access.mdx) for more details and specific +examples. + +## Apply a permission profile + +### Using a built-in profile + +To run an MCP server with unrestricted network access (the default): + +```bash +thv run --permission-profile network +``` + +To run an MCP server with no network access: + +```bash +thv run --isolate-network --permission-profile none +``` + +### Using a custom profile file + +To run an MCP server with your custom profile: + +```bash +thv run --isolate-network --permission-profile /path/to/custom-profile.json +``` + +## Security best practices + +When creating and using permission profiles: + +- Use the `none` profile when possible for MCP servers that don't require + network or file access +- Only grant necessary permissions +- Avoid enabling `network.outbound.insecure_allow_all`, as this allows + unrestricted outbound network access +- Review and test custom profiles thoroughly +- Keep permission profiles in version control to track changes and share them + with your team + +## Next steps + +- [Set up authentication](./auth.mdx) for user-level access control with OIDC + and Cedar policies + +## Related information + +- [`thv run` command reference](../reference/cli/thv_run.md) +- [Run MCP servers](./run-mcp-servers.mdx) +- [File system access](./filesystem-access.mdx) +- [Network isolation](./network-isolation.mdx) diff --git a/versioned_docs/version-1.1/toolhive/guides-cli/filesystem-access.mdx b/versioned_docs/version-1.1/toolhive/guides-cli/filesystem-access.mdx new file mode 100644 index 00000000..cd94c33c --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-cli/filesystem-access.mdx @@ -0,0 +1,255 @@ +--- +title: File system access +description: How to enable MCP server access to your host's file system in ToolHive. +--- + +Since ToolHive runs MCP servers in isolated containers, they don't have access +to your host's file system by default. However, some servers need to read or +write files on the host system to function properly or persist data. + +There are two ways to enable file system access for MCP servers in ToolHive: + +1. **The `--volume` flag**: Mount files using Docker volume syntax. + +2. **Permission profiles**: Create a custom permission profile that defines + which host paths the MCP server can read or write. + +## Choose a method + +When deciding how to enable file system access for an MCP server, consider the +differences between permission profiles and the `volume` flag: + +- If you use a permission profile, ToolHive mounts the paths you specify in the + `read` and `write` properties to the same location inside the container. The + MCP server will see files at the same path as on your host. If your server + expects files in a different location, it might not work properly. + +- With the `--volume` flag, you use Docker's syntax to set both the source path + on your host and the destination path inside the container. This gives you + more control over where files appear in the MCP server's environment. Use this + when your server needs files in a specific location. + +Choose the method that matches your server's requirements. Use permission +profiles for simple, direct mappings. Use the `--volume` flag when you need to +customize the destination path inside the container. + +## The `--volume` flag + +To enable file system access using the `--volume` flag, you specify the host +path and the container path in the `thv run` command. The syntax is the same as +Docker's volume syntax for bind mounts: + +```bash +thv run --volume :[:ro] +``` + +You can add the `--volume` flag multiple times to mount multiple paths. + +The optional `:ro` suffix makes the volume read-only in the container. This is +useful for sharing files without letting the MCP server modify them. For +example, to mount a host directory `/home/user/data` as read-only in the +container at `/data`, run: + +```bash +thv run --volume /home/user/data:/data:ro +``` + +To mount a host file `/home/user/config.json` as read-write in the container at +`/app/config.json`, run: + +```bash +thv run --volume /home/user/config.json:/app/config.json +``` + +### Use with built-in network profiles + +You can also use the `--volume` flag with built-in network profiles for a +flexible approach that doesn't require creating a custom profile file. + +For example, the AWS Diagram MCP server doesn't need any network connectivity, +but generates diagrams locally. You can use the built-in `none` profile to block +all network access and mount a directory for the generated diagrams: + +```bash +thv run --isolate-network --permission-profile none --volume /home/user/aws-diagrams:/tmp/generated-diagrams aws-diagram +``` + +This approach is useful when you need simple file system access alongside +standard network restrictions. + +## Permission profiles + +To enable file system access using a permission profile, create a custom profile +that specifies which host paths the MCP server can read or write. You can +include file system permissions alone or pair them with network permissions in +the same profile. + +:::note + +Paths in permission profiles must be absolute paths on your host system. +Relative paths and expanded paths (like `~/`) are not supported. Always use full +paths starting from the root directory (e.g., `/home/user/documents`). + +::: + +### File system-only profile + +Create a JSON file with your desired file system permissions: + +```json title="file-permissions.json" +{ + "read": ["/home/user/documents", "/var/log/app"], + "write": ["/home/user/output"] +} +``` + +This profile: + +- Mounts `/home/user/documents` and `/var/log/app` as read-only paths +- Mounts `/home/user/output` as a read-write path (note that `write` also + implies read access) + +### Paired with network permissions + +You can also pair file system permissions with network access in the same +profile: + +```json title="combined-permissions.json" +{ + "read": ["/home/user/config"], + "write": ["/home/user/data"], + "network": { + "outbound": { + "allow_host": ["api.example.com"], + "allow_port": [443] + } + } +} +``` + +### Apply the profile + +To run an MCP server with your profile: + +```bash +thv run --permission-profile ./file-permissions.json +``` + +For more details on permission profiles and network permissions, see +[custom permissions](./custom-permissions.mdx). + +## Examples + +Below are some examples of how to use file system access with some common MCP +servers in the ToolHive registry. + +:::tip + +The example MCP servers below don't require network access, and they have +restricted permission profiles in the ToolHive registry. To further secure these +examples, add the `--isolate-network` flag to block network access entirely. +This ensures the MCP server can only access the file system as specified in your +profile or volume mounts. + +::: + +### Filesystem MCP server + +The +[Filesystem MCP server](https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem) +lets you read and write files on your host system. By default, it expects paths +to be mounted under `/projects` inside the container. + +Here's how to mount two directories using the `--volume` flag, with one as +read-only: + +```bash +thv run --volume ~/documents:/projects/docs:ro --volume ~/code:/projects/code filesystem +``` + +You can achieve the same result using a permission profile. Create a file called +`filesystem-profile.json`: + +```json title="filesystem-profile.json" +{ + "read": ["/home/user/documents"], + "write": ["/home/user/code"] +} +``` + +Since permission profiles mount paths at the same location inside the container +as they exist on your host, you need to tell the filesystem server which base +directory to allow. Pass `/home/user` as an argument: + +```bash +thv run --permission-profile ./filesystem-profile.json filesystem -- /home/user +``` + +### Memory MCP server + +The +[Memory MCP server](https://github.com/modelcontextprotocol/servers/tree/main/src/memory) +uses a local knowledge graph to persist information across chats. + +To preserve data between runs, you need to mount a file. First, create an empty +JSON file, then mount it to the expected location inside the container: + +```bash +touch ./memory.json +thv run --volume ./memory.json:/app/dist/memory.json memory +``` + +### SQLite MCP server + +The [SQLite MCP server](https://github.com/StacklokLabs/sqlite-mcp) works with +database files on your host system. By default, it expects to find a database +file at `/database.db` inside the container: + +```bash +thv run --volume ~/my-database.db:/database.db sqlite +``` + +If you want to place the database file in a different location inside the +container, use the server's `--db` flag to specify the new path: + +```bash +thv run --volume ~/my-database.db:/data/my-database.db sqlite -- --db /data/my-database.db +``` + +## Next steps + +- [Exclude sensitive files](./thvignore.mdx) from MCP server access with + `.thvignore` +- [Restrict network access](./network-isolation.mdx) to control outbound + connections + +## Related information + +- [`thv run` command reference](../reference/cli/thv_run.md) +- [Run MCP servers](./run-mcp-servers.mdx) +- [Custom permissions](./custom-permissions.mdx) +- [Network isolation](./network-isolation.mdx) + +## Troubleshooting + +
+File system access issues + +If your MCP server can't access the file system as expected: + +1. Verify that the paths in your profile or volume flag are correct +2. Ensure the host paths exist and have the correct permissions + - The MCP server runs as a specific user inside the container, so the host + paths must be accessible to that user +3. Check that the permissions are set correctly (read/write) +4. Inspect the container's mounted paths to ensure they match your expectations: + + ```bash + docker inspect + ``` + + Look for the `Mounts` section to see how paths are mapped. + +5. Restart the server with the updated profile or corrected volume mount + +
diff --git a/versioned_docs/version-1.1/toolhive/guides-cli/group-management.mdx b/versioned_docs/version-1.1/toolhive/guides-cli/group-management.mdx new file mode 100644 index 00000000..e66dc32c --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-cli/group-management.mdx @@ -0,0 +1,132 @@ +--- +title: Organize servers into groups +description: How to organize MCP servers into logical groups and configure client access. +--- + +This guide explains how to organize your MCP servers into logical groups and +configure which groups your MCP clients can access. + +:::tip[New to groups?] + +If you're not sure whether groups are right for your use case, see +[Organizing MCP servers with groups](../concepts/groups.mdx) for an overview of +when and why to use them. + +::: + +:::info[What's the default behavior?] + +All MCP servers are automatically assigned to the `default` group unless you +specify otherwise. MCP clients configured without a specific group can access +all servers in the `default` group. + +::: + +## Create and organize groups + +### Create a group + +```bash +thv group create +``` + +For example, to create separate groups for different environments: + +```bash +thv group create development +thv group create production +``` + +### Run servers in a group + +When running an MCP server, specify the group using the `--group` flag: + +```bash +thv run --group development fetch +thv run --group production filesystem --volume /prod/repo:/projects:ro +``` + +:::info[What's happening?] + +When you specify a group: + +1. The server is assigned to that group and labeled accordingly +2. The server can only be accessed by clients configured for that group + +::: + +A single workload can only belong to one group at a time. To run multiple +instances of the same MCP server in different groups, use a unique name for each +instance: + +```bash +thv run --group development --name fetch-dev fetch +thv run --group production --name fetch-prod fetch +``` + +## Configure client access to groups + +You can configure MCP clients to access specific groups, giving you control over +which tools are available in different contexts. + +### Configure a client for a specific group + +When registering a client, you can specify which group it should access: + +```bash +thv client register --group development +``` + +This configures the client to only access servers in the `development` group. + +## Example workflows + +### Project-based organization + +```bash +# Create groups for different projects +thv group create webapp-frontend +thv group create webapp-backend + +# Run project-specific servers +thv run --group webapp-frontend mcp-react-tools +thv run --group webapp-backend mcp-database-tools + +# Configure clients with appropriate access +thv client register --client vscode --group webapp-frontend +thv client register --client cursor --group webapp-backend +``` + +## Manage groups + +### Remove a group + +Remove a group and move its servers to the default group: + +```bash +thv group rm development +``` + +Remove a group and delete all its servers: + +```bash +thv group rm development --with-workloads +``` + +:::warning + +Using `--with-workloads` permanently deletes all servers in the group. + +::: + +## Next steps + +- [Configure your AI client](./client-configuration.mdx) to connect to your + server groups +- [Manage secrets](./secrets-management.mdx) for the MCP servers in your groups + +## Related information + +- [Run MCP servers](run-mcp-servers.mdx) +- [`thv group` command reference](../reference/cli/thv_group.md) +- [`thv client` command reference](../reference/cli/thv_client.md) diff --git a/versioned_docs/version-1.1/toolhive/guides-cli/index.mdx b/versioned_docs/version-1.1/toolhive/guides-cli/index.mdx new file mode 100644 index 00000000..ddbcade7 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-cli/index.mdx @@ -0,0 +1,43 @@ +--- +title: Using the ToolHive CLI +description: + How-to guides for using the ToolHive command-line interface to run and manage + MCP servers. +--- + +import DocCardList from '@theme/DocCardList'; + +## Introduction + +The ToolHive CLI (`thv`) is a command-line tool that allows you to deploy and +manage MCP servers on your local machine or in development environments. It +provides quick deployment of MCP servers and supports advanced features like +custom permissions, network access filtering, and telemetry. + +It's designed for developers who prefer working in a terminal or need to +integrate MCP server management into scripts or automation workflows. + +:::info[Using ToolHive UI?] + +ToolHive UI includes and manages the CLI automatically. You don't need to +install the CLI separately. See +[CLI conflict resolution](./install.mdx#cli-conflict-resolution) if you have +both installed. + +::: + +## Where to start + +- **New to ToolHive CLI?** Follow the [Quickstart](./quickstart.mdx) to install + `thv` and run your first MCP server in minutes. +- **Already installed?** Jump to [Run MCP servers](./run-mcp-servers.mdx) to + start managing servers from the command line. +- **Managing agent skills?** See [Manage agent skills](./skills-management.mdx) + to install and publish reusable skills. +- **Building or automating?** See advanced workflows for [auth](./auth.mdx), + [CI/CD](./advanced-cicd.mdx), [container builds](./build-containers.mdx), and + more. + +## Contents + + diff --git a/versioned_docs/version-1.1/toolhive/guides-cli/install.mdx b/versioned_docs/version-1.1/toolhive/guides-cli/install.mdx new file mode 100644 index 00000000..95d0ac22 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-cli/install.mdx @@ -0,0 +1,473 @@ +--- +title: Install ToolHive +description: How to install, upgrade, and manage the ToolHive CLI. +--- + +This guide walks you through installing, upgrading, and managing the ToolHive +CLI ([`thv`](../reference/cli/thv.md)). + +![Latest version](https://img.shields.io/github/v/release/stacklok/toolhive?style=for-the-badge&label=Latest%20version&color=bddfc2) +![Release date](https://img.shields.io/github/release-date/stacklok/toolhive?display_date=published_at&style=for-the-badge&label=Released&color=bddfc2) + +## Prerequisites + +Before installing ToolHive, make sure your system meets these requirements: + +- **Operating systems**: macOS, Linux, or Windows +- **Container runtime**: + - Docker / Docker Desktop + - Podman / Podman Desktop + - Colima with Docker runtime + - Rancher Desktop with the dockerd/moby runtime (experimental) + +ToolHive requires minimal CPU, memory, and disk space. The exact requirements +depend on how many MCP servers you run and the resources they use. + +## Install ToolHive + +You can install ToolHive using several methods: + + + + +Homebrew is the easiest installation method on macOS or Linux: + +```bash +brew tap stacklok/tap +brew install thv +``` + + + + +WinGet is available by default on Windows, making this the easiest installation +method: + +```bash +winget install stacklok.thv +``` + +Open a new terminal window after installation to ensure the `thv` command is +available. + + + + +1. Visit the + [ToolHive releases page](https://github.com/stacklok/toolhive/releases/latest) + +2. Download the appropriate binary for your platform: + - `toolhive__darwin_amd64.tar.gz` for macOS (Intel) + - `toolhive__darwin_arm64.tar.gz` for macOS (Apple Silicon) + - `toolhive__linux_amd64.tar.gz` for Linux (x86_64) + - `toolhive__linux_arm64.tar.gz` for Linux (ARM64) + - `toolhive__windows_amd64.zip` for Windows (x86_64) + - `toolhive__windows_arm64.zip` for Linux (ARM64) + +3. Extract the archive and copy the binary to a directory in your PATH: + + macOS/Linux: + + ```bash + tar -xzf toolhive__.tar.gz + sudo install -m 0755 thv /usr/local/bin/ + ``` + + On Windows, extract the ZIP file to a folder and add that folder to your PATH + environment variable. + + + + +#### Prerequisites for building from source + +- Go 1.24 or newer +- Git +- Your `$GOPATH/bin` directory should be in your PATH + +#### Using Task (recommended) + +:::note + +The Task scripts currently only support macOS and Linux. Windows users should +use the pre-compiled binaries or build from source using Go tools. + +::: + +If you have [Task](https://taskfile.dev/installation/) installed: + +1. Clone the repository: + + ```bash + git clone https://github.com/stacklok/toolhive.git + cd toolhive + ``` + +2. Build and install the binary in your `$GOPATH/bin`: + + ```bash + task install + ``` + +#### Using Go tools + +1. Clone the repository: + + ```bash + git clone https://github.com/stacklok/toolhive.git + cd toolhive + ``` + +2. Build and install the binary in your `$GOPATH/bin`: + + ```bash + go install ./cmd/thv + ``` + + + + +## Verify your installation + +To verify that ToolHive is installed correctly: + +```bash +thv version +``` + +You should see output showing the version number, build date, and Git commit: + +```text title="ToolHive version output" +ToolHive v0.1.1 +Commit: 18956ca1710e11c9952d13a8dde039d5d1d147d6 +Built: 2025-06-30 13:59:34 UTC +Go version: go1.24.1 +Platform: darwin/arm64 +``` + +:::info[Privacy: Anonymous usage metrics] + +ToolHive collects anonymous usage metrics (tool call counts only) to help +improve the product. No personal information or sensitive data is collected. You +can disable this at any time using `thv config usage-metrics disable`. For more +information, see the [FAQ](../faq.mdx#does-toolhive-collect-any-data). + +::: + +## CLI conflict resolution + +If you have the ToolHive UI installed, it automatically manages the CLI for +version compatibility. ToolHive UI creates a symlink to its bundled CLI and +configures your shell's PATH, so you don't need to install the CLI separately. + +Running a standalone CLI binary (installed via Homebrew, WinGet, or manually) +while ToolHive UI is installed shows a conflict error: + +```text title="CLI conflict error" +Error: CLI conflict detected + +The ToolHive Desktop application manages a CLI installation at: + /Applications/ToolHive Studio.app/Contents/Resources/bin/darwin-arm64/thv + +You are running a different CLI binary at: + /usr/local/bin/thv + +To avoid conflicts, please use the desktop-managed CLI or uninstall +the ToolHive Desktop application. + +To use the desktop-managed CLI, ensure your PATH includes: + ~/.toolhive/bin + +Or run the desktop CLI directly: + ~/.toolhive/bin/thv [command] + +Desktop version: 0.8.3 +``` + +### Resolving the conflict + +If you see this error, you have two options: + +1. **Use the UI-managed CLI (recommended)**: Open a new terminal window to pick + up the PATH changes. The `thv` command should now use the UI-managed CLI. + +2. **Uninstall the standalone CLI**: If you want to use only the UI-managed CLI, + uninstall the standalone version: + - Homebrew: `brew uninstall thv` + - WinGet: `winget uninstall stacklok.thv` + - Manual: Remove the binary from your PATH + +:::note[Debugging override] + +For debugging purposes, you can bypass the conflict check by setting +`TOOLHIVE_SKIP_DESKTOP_CHECK=1`. This is not recommended for normal use as it +may cause version compatibility issues. + +::: + +## Upgrade ToolHive + +ToolHive automatically checks for updates and notifies you when a new version is +available. When you run a ToolHive command, it displays a message if an update +exists. + +To upgrade ToolHive: + + + + +If you installed ToolHive via Homebrew, upgrade it with: + +```bash +brew upgrade thv +``` + + + + +:::note + +On Windows, you must stop all running MCP servers before upgrading ToolHive, +otherwise the upgrade will fail because Windows locks the executable while it +runs. + +Run `thv stop --all` to stop all running servers. After you complete the +upgrade, restart them using `thv restart `. + +::: + +If you installed ToolHive via WinGet, upgrade it with: + +```bash +winget upgrade stacklok.thv +``` + + + + +Follow the same steps as the [initial installation](#install-toolhive), +downloading the latest release and overwriting the previous binary. + + + + +If you built ToolHive from source, upgrade it by pulling the latest changes and +rebuilding: + +#### Using Task: + +```bash +git pull + +task install +``` + +#### Using Go tools + +```bash +git pull + +go install ./cmd/thv +``` + + + + +## Get help with ToolHive commands + +ToolHive has built-in help for all commands: + +```bash +# General help +thv --help + +# Help for a specific command +thv --help +``` + +For detailed documentation on each command, see the +[CLI reference documentation](../reference/cli/thv.md). + +## Enable shell completion + +ToolHive can generate auto-completion scripts for your shell to make it easier +to use. The `thv completion` command generates scripts for bash, zsh, fish, and +PowerShell. + +Each shell has different requirements for where to store completion scripts and +how to enable them. The help output for each shell provides specific +instructions: + +```bash +# Get help on completion options +thv completion --help + +# Get specific instructions for your shell +thv completion bash --help +thv completion zsh --help +thv completion fish --help +thv completion powershell --help +``` + +## Uninstall ToolHive + +To uninstall ToolHive: + +1. First, remove any MCP servers managed by ToolHive: + + ```bash + # List running servers + thv list + # Stop and remove each server + thv stop --all + thv rm + ``` + +2. Remove all ToolHive configuration and log files: + + ```bash + # Remove the secrets encryption password entry from your OS keyring + thv secret reset-keyring + + # Delete the ToolHive configuration and log files + # macOS: + rm -rf ~/Library/Application\ Support/toolhive/ + + # Linux: + rm -rf ~/.config/toolhive/ + rm -rf ~/.local/share/toolhive/ + rm -rf ~/.local/state/toolhive/ + + # Windows: + Remove-Item "$env:LOCALAPPDATA\toolhive" -Recurse -Force + ``` + +3. Remove the ToolHive CLI: + +{/* prettier-ignore */} + + + + If you installed ToolHive via Homebrew, uninstall it with: + + ```bash + brew uninstall thv + ``` + + + + + If you installed ToolHive via WinGet, uninstall it with: + + ```bash + winget uninstall stacklok.thv + ``` + + + + + Delete the binary from your PATH: + + ```bash + sudo rm /usr/local/bin/thv + ``` + + + + + Remove the binary from your `$GOPATH`: + + ```bash + rm $(go env GOPATH)/bin/thv + ``` + + + + + +## Next steps + +Now that you have ToolHive installed, you can start using it to run and manage +MCP servers. See [Explore the registry](./registry.mdx) and +[Run MCP servers](./run-mcp-servers.mdx) to get started. + +## Related information + +- Quickstart: [Getting started with the ToolHive CLI](./quickstart.mdx) +- [`thv` CLI reference](../reference/cli/thv.md) +- [Client configuration](./client-configuration.mdx) +- [Secrets management](./secrets-management.mdx) + +## Troubleshooting + +
+Permission denied errors + +If you see "permission denied" errors when running ToolHive: + +1. Make sure the binary is executable: + + ```bash + chmod +x /path/to/thv + ``` + +2. If using Docker on Linux, make sure your user has permission to access the + Docker socket: + + ```bash + sudo usermod -aG docker $USER + ``` + + (Log out and back in or run `newgrp docker` for this to take effect) + + See + [Docker documentation](https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user) + for more details. + +
+ +
+Upgrade error on Windows + +If you encounter an error when upgrading ToolHive on Windows, it may be due to +the ToolHive executable being locked by a running MCP server proxy. + +```text title="Error example" +An unexpected error occurred while executing the command: +remove: Access is denied.: "C:\Users\USERNAME\AppData\Local\Microsoft\WinGet\Packages\stacklok.thv_Microsoft.Winget.Source_8wekyb3d8bbwe\thv.exe" +Uninstall failed with exit code: 0x8a150003 : Executing command failed +``` + +To resolve this: + +1. Stop all running MCP servers: + + ```powershell + thv stop --all + ``` + +2. After stopping all servers, run the upgrade command again: + + ```powershell + winget upgrade stacklok.thv + ``` + +3. If you still encounter issues, check if any ToolHive processes are still + running in the background. You can use Task Manager to end any lingering + `thv.exe` processes. + +4. Restart your MCP servers: + + ```powershell + thv list --all + thv restart + # repeat for each server + ``` + +
+ +### Other issues + +For other installation issues, check the +[GitHub issues page](https://github.com/stacklok/toolhive/issues) or join the +[Discord community](https://discord.gg/stacklok). diff --git a/versioned_docs/version-1.1/toolhive/guides-cli/manage-mcp-servers.mdx b/versioned_docs/version-1.1/toolhive/guides-cli/manage-mcp-servers.mdx new file mode 100644 index 00000000..d050f3b7 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-cli/manage-mcp-servers.mdx @@ -0,0 +1,168 @@ +--- +title: Manage servers +description: How to monitor and manage the lifecycle of MCP servers using ToolHive. +--- + +## Monitoring + +ToolHive provides visibility into the status of your MCP servers. You can check +the status of running servers and view their logs using the ToolHive CLI. + +### List running servers + +To see all currently running MCP servers: + +```bash +thv list +``` + +This shows details about each running server, including its name, package +(container image), status, URL for connecting clients, group, and when it was +created. + +To include stopped servers in the list: + +```bash +thv list --all +``` + +### View server logs + +To view logs for an MCP server, use the +[`thv logs`](../reference/cli/thv_logs.md) command. You can optionally follow +the logs with the `--follow` option, which shows the most recent log entries and +updates in real time. + +#### Container logs + +For local servers, view the container logs: + +```bash +thv logs [--follow] +``` + +#### Proxy logs + +All servers (both local and remote) run through a proxy process managed by +ToolHive. To view the proxy logs instead of the container logs, use the +`--proxy` flag: + +```bash +thv logs --proxy [--follow] +``` + +For remote servers, the `--proxy` flag is required since there is no container +to read logs from. + +Alternatively, you can check the log files directly in the ToolHive application +directory. The path depends on your platform: + +- **macOS**: `~/Library/Application Support/toolhive/logs/.log` +- **Linux**: `~/.local/share/toolhive/logs/.log` +- **Windows**: `%LOCALAPPDATA%\toolhive\logs\.log` + +The specific log file path is displayed when you start a server with +[`thv run`](../reference/cli/thv_run.md). + +## Lifecycle management + +MCP servers can be started, stopped, restarted, and removed using the ToolHive +CLI. The commands are similar to Docker commands, but they're designed to work +with the ToolHive CLI and MCP servers. + +### Stop a server + +To stop a running MCP server: + +```bash +thv stop +``` + +This stops the server and the associated proxy process, removes the MCP server's +entry from your configured clients, but keeps the container for future use. For +remote servers, this terminates the proxy process but preserves the +configuration. + +Add the `--group` flag to stop all servers in a specific group: + +```bash +thv stop --group +``` + +Add the `--all` flag to stop all running servers. + +### Restart a server + +To start a stopped MCP server and add it back to your configured clients: + +```bash +thv start +``` + +For remote servers, restarting will: + +1. Recreate the proxy process +2. Re-establish connection to the remote server +3. Re-authenticate with the remote server (triggers new OAuth flow) + +Add the `--group` flag to start all servers in a specific group: + +```bash +thv start --group +``` + +### Remove a server + +To remove an MCP server: + +```bash +thv rm +``` + +This removes the container and cleans up the MCP server's entry in your +configured clients. If the server is still running, it will be stopped as part +of the removal. For remote servers, this removes the proxy process, +configuration, and stored authentication tokens. + +Add the `--group` flag to remove all servers in a specific group: + +```bash +thv rm --group +``` + +:::note + +If you use `docker rm` to remove an MCP container that ToolHive created, it +won't clean up the MCP server's entry in your configured clients. Use +[`thv rm`](../reference/cli/thv_rm.md) to make sure the entry is removed. + +::: + +### Remote server authentication + +Remote servers with OAuth authentication will automatically refresh tokens when +they expire during normal operation. However, starting a remote server always +triggers a new OAuth authentication flow: + +```bash +thv start +``` + +This will always prompt for re-authentication, even if valid tokens exist. + +## Next steps + +- [Organize servers into groups](./group-management.mdx) to manage related + servers together +- [Monitor server activity](./telemetry-and-metrics.mdx) with OpenTelemetry and + Prometheus +- [Configure your AI client](./client-configuration.mdx) to connect to your + servers + +## Related information + +- [`thv list` command reference](../reference/cli/thv_list.md) +- [`thv logs` command reference](../reference/cli/thv_logs.md) +- [`thv stop` command reference](../reference/cli/thv_stop.md) +- [`thv start` command reference](../reference/cli/thv_start.md) +- [`thv rm` command reference](../reference/cli/thv_rm.md) diff --git a/versioned_docs/version-1.1/toolhive/guides-cli/network-isolation.mdx b/versioned_docs/version-1.1/toolhive/guides-cli/network-isolation.mdx new file mode 100644 index 00000000..7d768bed --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-cli/network-isolation.mdx @@ -0,0 +1,360 @@ +--- +title: Network isolation +description: How to configure network isolation for MCP servers in ToolHive +--- + +Most MCP servers require network access to function properly—for example, to +access APIs, download data, or communicate with other services. However, +malicious or misconfigured servers can also exfiltrate sensitive data or +download unwanted content. + +When you run an MCP server in ToolHive, you can optionally enable _network +isolation_. This feature restricts the MCP server's network access to only the +resources you specify. + +## Enable network isolation + +To enforce network access rules, use the `--isolate-network` flag when running +the MCP server. Network rules can come from: + +- The MCP server's default registry permissions +- A custom permission profile you create + +```bash +thv run --isolate-network [--permission-profile
] +``` + +:::tip + +You can combine file system and network permissions in the same permission +profile. For details on creating profiles that include both types of +permissions, see [custom permissions](./custom-permissions.mdx). + +::: + +When you enable network isolation, ToolHive creates a secure network +architecture around your MCP server that includes several components working +together to control network access. + +### Network architecture components + +Along with the main MCP server container, ToolHive launches additional +containers to manage network traffic: + +- An egress proxy container that filters outgoing network traffic +- A DNS container that provides controlled domain name resolution +- An ingress proxy container that handles incoming requests (only for MCP + servers using SSE or Streamable HTTP transport; stdio MCP servers don't need + this since they don't expose ports) + +### Network topology + +ToolHive creates two separate networks in the container runtime: + +- A shared external network (`toolhive-external`) that connects to your host's + network +- An internal network (`toolhive--internal`) for each MCP server + that isolates it from external access + +The MCP server container connects only to the internal network, while the proxy +and DNS containers connect to both networks. This design ensures that all +network traffic flows through controlled points, allowing ToolHive to enforce +the access rules you specify in your permission profile. + +The following diagrams show how network traffic flows through the isolation +architecture for different transport types: + + + + +For MCP servers using stdio transport, the ToolHive proxy process communicates +directly with the MCP server container through standard input and output. All +outbound network requests from the MCP server flow through the egress proxy and +DNS containers: + +```mermaid +architecture-beta + service mcp_client(server)[MCP client] + service toolhive_proxy(server)[ToolHive HTTP proxy process] + + group container_runtime[Container runtime] + group thv_internal[Isolated network] in container_runtime + service egress(server)[Egress proxy container] in container_runtime + service mcp_server(server)[MCP server container] in thv_internal + service dns_container(server)[DNS container] in container_runtime + + group external[External Resources] + service external_service(internet)[External service] in external + service external_dns(internet)[External DNS] in external + + junction outbound in container_runtime + + mcp_client:R --> L:toolhive_proxy + toolhive_proxy:R --> L:mcp_server + mcp_server:R -- L:outbound + egress:B <-- T:outbound + dns_container:T <-- B:outbound + egress:R --> L:external_service + dns_container:R --> L:external_dns +``` + + + + +For MCP servers using SSE or Streamable HTTP transport, ToolHive includes an +additional ingress proxy container. This proxy handles incoming HTTP requests +and ensures the MCP server remains isolated from direct external access: + +```mermaid +architecture-beta + service mcp_client(server)[MCP client] + service toolhive_proxy(server)[ToolHive HTTP proxy process] + + group container_runtime[Container runtime] + group thv_internal[Isolated network] in container_runtime + service ingress(server)[Ingress proxy container] in container_runtime + service egress(server)[Egress proxy container] in container_runtime + service mcp_server(server)[MCP server container] in thv_internal + service dns_container(server)[DNS container] in container_runtime + + group external[External resources] + service external_service(internet)[External service] in external + service external_dns(internet)[External DNS] in external + + junction outbound in container_runtime + + mcp_client:R --> L:toolhive_proxy + toolhive_proxy:R --> L:ingress + ingress:R --> L:mcp_server + mcp_server:R -- L:outbound + egress:B <-- T:outbound + dns_container:T <-- B:outbound + egress:R --> L:external_service + dns_container:R --> L:external_dns +``` + + + + +:::info[Important] + +Network isolation supports HTTP and HTTPS protocols. If your MCP server needs to +use other protocols (like direct TCP connections for database access), run it +without the `--isolate-network` flag and rely on the container's built-in +isolation instead. + +::: + +## Example: Enable network isolation using registry defaults + +Many MCP servers in the ToolHive registry have default permission profiles that +allow access to the specific resources they need. For example, the `atlassian` +MCP server has a default profile that allows access to Atlassian services. + +First, check the registry to see the default permissions for the `atlassian` MCP +server: + +```bash +thv registry info atlassian +``` + +Look for the `Permissions` section in the output: + +```text +Permissions: + Network: + Allow Host: .atlassian.net, .atlassian.com + Allow Port: 443 +``` + +To run the `atlassian` MCP server with these default permissions and enable +network isolation, use the following command: + +```bash +thv run --isolate-network atlassian +``` + +## Example: Customize network access + +The GitHub MCP server in the registry has a default profile that allows access +to `github.com`, but you might need to customize it for a self-hosted GitHub +Enterprise instance: + +```json title="github-enterprise-profile.json" +{ + "network": { + "outbound": { + "insecure_allow_all": false, + "allow_host": ["github.example.com"], + "allow_port": [443] + } + } +} +``` + +Run the GitHub MCP server with this profile: + +```bash +thv run --isolate-network --permission-profile ./github-enterprise-profile.json --secret github,target=GITHUB_PERSONAL_ACCESS_TOKEN github +``` + +## Example: Combined network and file system permissions + +Some MCP servers need both network restrictions and file system access. For +example, the `aws-diagram` MCP server generates diagrams locally but doesn't +need network access: + +```json title="aws-diagram-profile.json" +{ + "write": ["/tmp/generated-diagrams"], + "network": { + "outbound": { + "insecure_allow_all": false, + "allow_host": [], + "allow_port": [] + } + } +} +``` + +This profile: + +- Blocks all network access (equivalent to the `none` built-in profile) +- Allows the server to write generated diagrams to `/tmp/generated-diagrams` + +Run the server with this combined profile: + +```bash +thv run --isolate-network --permission-profile ./aws-diagram-profile.json aws-diagram +``` + +### Alternative: Using built-in profiles with volume mounts + +You can achieve the same result without creating a custom profile file by using +the built-in `none` profile and the `--volume` flag: + +```bash +thv run --isolate-network --permission-profile none --volume /home/user/aws-diagrams:/tmp/generated-diagrams aws-diagram +``` + +This approach is more flexible since you can easily change the host directory +without editing a profile file. + +## Accessing other workloads on the same container network + +ToolHive allows you to configure both outbound and inbound network access for +MCP servers. This is commonly needed when your MCP server needs to communicate +with databases, APIs, or other services that are running on your local host +during development, or when other containers need to communicate with your MCP +server. + +### Outbound access: MCP server to other workloads + +To allow an MCP server to access other workloads on the same network, you need +to configure outbound network isolation to include the appropriate hostnames and +ports. + +For example, in Docker environments, you can add `host.docker.internal` to +access services on the host. `host.docker.internal` is a special hostname +provided by Docker that resolves to the host machine's IP address from within +containers. + +Create a permission profile that allows this hostname and the required port: + +```json title="internal-access-profile.json" +{ + "network": { + "outbound": { + "insecure_allow_all": false, + "allow_host": ["host.docker.internal"], + "allow_port": [3000] + } + } +} +``` + +Run the MCP server with this profile: + +```bash +thv run --isolate-network --permission-profile ./internal-access-profile.json +``` + +### Inbound access: Other containers to MCP server + +By default, the ingress proxy only allows traffic from the container's own +hostname, `localhost`, and `127.0.0.1`. If you need to allow other containers or +workloads to communicate with your MCP server, configure the +`network.inbound.allow_host` setting in your permission profile. + +This is useful when: + +- Other containers need to call your MCP server's API +- You're running multiple services that need to communicate with each other +- You need to allow traffic from specific internal hostnames or domains + +Create a permission profile that allows specific inbound hostnames: + +```json title="inbound-access-profile.json" +{ + "network": { + "inbound": { + "allow_host": ["host.docker.internal", "localhost"] + } + } +} +``` + +Run the MCP server with this profile: + +```bash +thv run --isolate-network --permission-profile ./inbound-access-profile.json +``` + +:::info + +If no `network.inbound` configuration is specified, the ingress proxy uses the +default behavior of allowing traffic only from the container's own hostname, +`localhost`, and `127.0.0.1`. + +::: + +## Next steps + +- [Set up authentication](./auth.mdx) for user-level access control with OIDC + and Cedar policies + +## Related information + +- [`thv run` command reference](../reference/cli/thv_run.md) +- [Run MCP servers](./run-mcp-servers.mdx) +- [Custom permissions](./custom-permissions.mdx) +- [File system access](./filesystem-access.mdx) + +## Troubleshooting + +
+Network connectivity issues + +If your MCP server can't connect to external services: + +1. Verify that your profile allows the necessary hosts and ports +2. Check that the transport protocol (TCP/UDP) is allowed +3. Check the logs of the egress proxy container for blocked requests: + + ```bash + docker logs -egress + ``` + + Look for messages indicating denied connections. + +4. Try temporarily using the default `network` profile to confirm it's a + permissions issue: + + ```bash + thv run --isolate-network --permission-profile network + ``` + +5. If the default profile works, review the egress proxy logs to identify which + specific permissions are missing in your custom profile. + +
diff --git a/versioned_docs/version-1.1/toolhive/guides-cli/quickstart.mdx b/versioned_docs/version-1.1/toolhive/guides-cli/quickstart.mdx new file mode 100644 index 00000000..6f0fb50b --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-cli/quickstart.mdx @@ -0,0 +1,345 @@ +--- +title: 'Quickstart: ToolHive CLI' +sidebar_label: Quickstart +description: + A step-by-step guide to installing the ToolHive CLI and running your first MCP + server. +--- + +In this tutorial, you'll install the ToolHive command-line application and run +your first MCP server. By the end, you'll have a working MCP server that can +fetch content from websites and be used by AI applications like GitHub Copilot +or Cursor. + +## What you'll learn + +- How to install ToolHive on your system +- How to find available MCP servers +- How to run an MCP server +- How to verify the server is working +- How to use the server with an AI client application + +## Prerequisites + +Before starting this tutorial, make sure you have: + +- [Docker](https://docs.docker.com/get-docker/) or + [Podman](https://podman-desktop.io/downloads) or + [Colima](https://github.com/abiosoft/colima) installed and running +- A [supported MCP client](../reference/client-compatibility.mdx) like GitHub + Copilot in VS Code, Cursor, Claude Code, and more + +## Step 1: Install ToolHive + +First, install ToolHive on your system. ToolHive provides the `thv` command-line +tool that manages MCP servers in secure containers. + +For detailed installation instructions, see the +[installing ToolHive](../guides-cli/install.mdx) guide. Here's a quick summary: + + + + +```bash +brew tap stacklok/tap +brew install thv +``` + + + + +```bash +winget install stacklok.thv +``` + +Open a new terminal window after installation to ensure the `thv` command is +available. + + + + +Download the appropriate binary for your platform from the +[ToolHive releases page](https://github.com/stacklok/toolhive/releases/latest). + +```bash +# Extract the archive +tar -xzf toolhive__.tar.gz +# Move the binary to a directory in your PATH +sudo mv thv /usr/local/bin/ +# Make it executable (if needed) +sudo chmod +x /usr/local/bin/thv +``` + + + + +### Verify your installation + +After installing, verify that ToolHive is working correctly: + +```bash +thv version +``` + +You should see output similar to this: + +```text +ToolHive v0.1.1 +Commit: 18956ca1710e11c9952d13a8dde039d5d1d147d6 +Built: 2025-06-30 13:59:34 UTC +Go version: go1.24.1 +Platform: darwin/arm64 +``` + +This confirms ToolHive is installed and ready to use. + +## Step 2: Register your client + +Next, run the ToolHive client setup command to register your MCP client: + +```bash +thv client setup +``` + +Select one or more clients from the list using the spacebar to toggle selection. +Press Enter to confirm your selection. + +:::info[What's happening?] + +When you run the setup command, ToolHive automatically finds +[supported clients](../reference/client-compatibility.mdx) on your system. When +you register a client, ToolHive automatically configures it to use MCP servers +that you run. This means you don't have to manually configure the client to +connect to the MCP server. + +::: + +Confirm that your client is registered successfully: + +```bash +thv client status +``` + +You should see output similar to this: + +```text +┌────────────────┬───────────┬────────────┐ +│ CLIENT TYPE │ INSTALLED │ REGISTERED │ +├────────────────┼───────────┼────────────┤ +│ vscode │ ✅ Yes │ ❌ No │ +└────────────────┴───────────┴────────────┘ +``` + +## Step 3: Find an MCP server to run + +See what MCP servers are available in the registry: + +```bash +thv registry list +``` + +You'll see output similar to this: + +```text +NAME DESCRIPTION TIER STARS PULLS +atlassian Model Context Protocol (MCP) server for Atlassian product... Community 2194 7789 +fetch A Model Context Protocol server that provides web content... Community 56714 9078 +github The GitHub MCP Server provides seamless integration with ... Official 16578 5000 +notion Official Notion MCP server. Official 2358 1109 +... +``` + +This shows all the MCP servers available in the ToolHive registry. + +:::info[What's happening?] + +ToolHive maintains a curated registry of MCP servers that have been verified to +work correctly. The registry includes information about what each server does +and how to use it. + +::: + +For this tutorial, you'll use the "fetch" server, which is a simple MCP server +that lets AI agents get the contents of a website. To learn more about the +server before running it, run: + +```bash +thv registry info fetch +``` + +This shows you detailed information about the server, including what tools it +provides and any configuration options. + +## Step 4: Run the Fetch MCP server + +Now, run the Fetch server: + +```bash +thv run fetch +``` + +ToolHive will pull the container image and start the server. You'll see output +similar to this: + +```text +8:41AM INF MCP server ghcr.io/stackloklabs/gofetch/server:latest is verified successfully +8:41AM INF Image ghcr.io/stackloklabs/gofetch/server:latest has 'latest' tag, pulling to ensure we have the most recent version... +8:41AM INF Pulling image: ghcr.io/stackloklabs/gofetch/server:latest +Pulling from stackloklabs/gofetch/server: latest +Digest: sha256:b9cbe3a8367f39e584d3fdd96d9c5046643c5f4798c3372b0c9049ece202cdef +Status: Image is up to date for ghcr.io/stackloklabs/gofetch/server:latest +8:41AM INF Successfully pulled image: ghcr.io/stackloklabs/gofetch/server:latest +8:41AM INF Using target port: 15266 +8:41AM INF Logging to: ~/Library/Application Support/toolhive/logs/fetch.log +8:41AM INF MCP server is running in the background (PID: 16834) +8:41AM INF Use 'thv stop fetch' to stop the server +``` + +:::info[What's happening?] + +When you run an MCP server, ToolHive: + +- Verifies the MCP server image provenance (if attestation information is + available in the registry) +- Downloads the container image +- Sets up the container with the necessary security settings and starts it in + the background +- Sets up a reverse proxy that lets your AI client applications communicate with + the server + +::: + +## Step 5: Verify the server is running + +Check that the server is running: + +```bash +thv list +``` + +You should see output similar to this: + +```text +NAME PACKAGE STATUS URL PORT TOOL TYPE CREATED AT +fetch ghcr.io/stackloklabs/gofetch/server:latest running http://127.0.0.1:15266/mcp 15266 mcp 2025-07-10 08:41:56 -0400 EDT +``` + +This confirms that the fetch server is running and available on port 15266. + +:::info[What's happening?] + +ToolHive keeps track of all the MCP servers it's managing. The +[`list`](../reference/cli/thv_list.md) command shows you which servers are +running and how they're configured. + +::: + +## Step 6: Use the MCP server with your AI client + +Now that your MCP server is running, you can use it with your AI client +application. Open your supported client (VS Code, Cursor, etc.) and ask the AI +to fetch content from a website. Note that you might need to restart your client +for the changes to take effect. + +For example, try asking: "Can you fetch the content from https://toolhive.dev +and summarize it for me?" + +The AI should be able to use the Fetch MCP server to retrieve the content and +provide a summary. + +:::info[What's happening?] + +When you ask the AI to fetch content, it detects that it needs external data. It +discovers the fetch tool provided by your MCP server, calls the tool with the +URL you specified, receives the webpage content, and then processes that content +to create a summary. + +::: + +## Step 7: Stop the server when you're done + +When you're finished using the server, you can stop it: + +```bash +thv stop fetch +``` + +If you want to remove it completely: + +```bash +thv rm fetch +``` + +:::info[What's happening?] + +Stopping a server pauses it and terminates the associated proxy process but +keeps the container around so you can start it quickly later using +[`thv start`](../reference/cli/thv_start.md) or +[`thv run`](../reference/cli/thv_run.md). Removing a server completely deletes +the container, freeing up resources. + +::: + +## What's next? + +Congratulations! You've successfully installed ToolHive and run your first MCP +server. Here are some next steps to explore: + +- Try running other MCP servers from the registry with + [`thv registry list`](../reference/cli/thv_registry_list.md) and + [`thv run`](../reference/cli/thv_run.md) +- Learn about [secrets management](../guides-cli/secrets-management.mdx) for MCP + servers that require authentication +- Explore [custom permissions](../guides-cli/custom-permissions.mdx) for MCP + servers +- Learn how to + [share and reuse server configurations](../guides-cli/run-mcp-servers.mdx#share-and-reuse-server-configurations) + using the export and import functionality +- Set up [shell completion](../guides-cli/install.mdx#enable-shell-completion) + to make ToolHive commands easier to use +- Learn how to [upgrade ToolHive](../guides-cli/install.mdx#upgrade-toolhive) + when new versions are available + +:::tip[Using ToolHive with a team?] + +ToolHive Community is great for individual use. When your organization needs +centralized governance, IdP integration (Okta, Entra ID), and hardened +production-ready MCP servers, that's where Stacklok Enterprise comes in. + +[See what's included in Stacklok Enterprise →](../enterprise.mdx) + +::: + +## Troubleshooting + +
+Server fails to start + +If the server fails to start, check: + +- Is Docker, Podman, or Colima running? +- Do you have internet access to pull the container image? +- Is the port already in use by another application? + +Try running with a specific port: + +```bash +thv run --proxy-port 8081 fetch +``` + +
+ +
+Client can't use the server + +If your AI client application can't use the server: + +- Make sure your client is registered with ToolHive (see Step 2) +- Check that your client is supported +- Restart your client to pick up the new configuration +- Verify the server is running with [`thv list`](../reference/cli/thv_list.md) + +
+ +If you encounter any problems, please join our +[Discord community](https://discord.gg/stacklok) for help. diff --git a/versioned_docs/version-1.1/toolhive/guides-cli/registry.mdx b/versioned_docs/version-1.1/toolhive/guides-cli/registry.mdx new file mode 100644 index 00000000..84a76aad --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-cli/registry.mdx @@ -0,0 +1,397 @@ +--- +title: Explore the registry +description: How to use the built-in registry to find MCP servers. +--- + +ToolHive includes a built-in registry of MCP servers with verified +configurations that meet a +[minimum quality standard](../concepts/registry-criteria.mdx), allowing you to +discover and deploy high-quality tools effortlessly. Simply select one from the +list and run it securely with a single command. + +## Find MCP servers + +To list all MCP servers in the ToolHive registry, run: + +```bash +thv registry list +``` + +This command displays a list of servers with their name, description, tier +(official or community), and the number of stars and downloads to help you +identify the most popular and useful servers. + +Example output: + +```text +NAME DESCRIPTION TIER STARS PULLS +atlassian Model Context Protocol (MCP) server for Atlassian product... Community 2194 7789 +elasticsearch Connect to your Elasticsearch data directly from any MCP ... Official 305 5429 +everything This MCP server attempts to exercise all the features of ... Community 56714 10441 +fetch A Model Context Protocol server that provides web content... Community 56714 9078 +filesystem Node.js server implementing Model Context Protocol (MCP) ... Community 56714 14041 +firecrawl A powerful web scraping and content extraction MCP server... Official 3605 7630 +git A Model Context Protocol server for Git repository intera... Community 56714 7000 +github The GitHub MCP Server provides seamless integration with ... Official 16578 5000 +grafana A Model Context Protocol (MCP) server for Grafana that pr... Official 1014 4900 +k8s MKP is a Model Context Protocol (MCP) server for Kubernet... Community 32 8064 + +<... trimmed for brevity ...> +``` + +You can also search by keyword to find servers related to a specific topic or +capability: + +```bash +thv search +``` + +For example, to locate servers related to GitHub: + +```bash +thv search github +``` + +## View server details + +To view detailed information about a specific MCP server, run: + +```bash +thv registry info +``` + +For example: + +```bash +thv registry info github +``` + +ToolHive provides the server's description, available tools, configuration +options, and other metadata. + +By default, ToolHive displays the server's configuration in a human-readable +format. To view the configuration in JSON format, use the `--format` option: + +```bash +thv registry info --format json +``` + +### Example output + +```yaml {1,11,19,24} showLineNumbers +Name: github +Image: ghcr.io/github/github-mcp-server:latest +Description: The GitHub MCP Server provides seamless integration with GitHub APIs, enabling advanced automation and interaction capabilities for developers and tools +Tier: Official +Status: Active +Transport: stdio +Repository URL: https://github.com/github/github-mcp-server +Has Provenance: Yes +Popularity: 13894 stars, 5000 pulls +Last Updated: 2025-05-20T00:21:46Z +Tools: + - get_me + - get_issue + - create_issue + - add_issue_comment + - list_issues +<... trimmed for brevity ...> + +Environment Variables: + - GITHUB_PERSONAL_ACCESS_TOKEN (required): GitHub personal access token with appropriate permissions + - GH_HOST: GitHub Enterprise Server hostname (optional) +Tags: + api, create, fork, github, list, pull-request, push, repository, search, update, issues +Permissions: + Network: + Allow Transport: tcp + Allow Host: docs.github.com, github.com + Allow Port: 443 +Example Command: + thv run github +``` + +This information helps you understand the server's capabilities, requirements, +and security profile before running it. + +- **Server name** (line 1): The server name to use with the + [`thv run`](../reference/cli/thv_run.md) command +- **Metadata** (lines 2-10): Details about the server, including the image name, + description, status, transport method, repository URL, whether the server has + SLSA provenance available for verification, and popularity +- **Tools list** (line 11): The list of tools this MCP server provides +- **Configuration** (line 19): Required and optional environment variables + needed to run the server +- **Permissions** (line 24): The permission profile applied to the server, + including file system and network access (see + [Custom permissions](./custom-permissions.mdx)) + +## Use a custom registry + +By default, ToolHive uses a built-in registry of verified MCP servers. You can +configure ToolHive to use a custom registry instead. This is useful for +organizations that want to maintain their own private registry of MCP servers. + +ToolHive supports two types of custom registries: + +- **File-based registries**: JSON files that follow either the + [ToolHive registry schema](../reference/registry-schema-toolhive.mdx) or the + [upstream registry schema](../reference/registry-schema-upstream.mdx) +- **API-based registries**: REST API endpoints that implement the + [MCP Registry API specification](../guides-registry/index.mdx), which use the + [upstream registry schema](../reference/registry-schema-upstream.mdx) + +Once you configure a custom registry, ToolHive uses it for all commands that +interact with the registry, such as `thv registry list`, `thv registry info`, +and `thv run`. + +### Set a remote registry + +To configure ToolHive to use a remote registry, set the registry URL or API +endpoint: + +```bash +thv config set-registry +``` + +The CLI automatically detects the registry type: + +- URLs ending with `.json` are treated as static registry files +- Other URLs are probed to detect MCP Registry API endpoints, falling back to + static files if the probe fails +- Local paths are treated as local registry files + +Examples: + +```bash +# Static registry file +thv config set-registry https://example.com/registry.json + +# API endpoint +thv config set-registry https://registry.example.com +``` + +### Set a local registry file + +To configure ToolHive to use a local registry file, set the file path: + +```bash +thv config set-registry +``` + +For example: + +```bash +thv config set-registry /path/to/local-registry.json +``` + +### Check the current registry + +To see which registry is currently configured: + +```bash +thv config get-registry +``` + +This displays the configured registry URL, API endpoint, or file path. If no +custom registry is configured, this command indicates that the built-in registry +is being used. + +### Revert to the built-in registry + +To remove the custom registry configuration and revert to using the built-in +registry: + +```bash +thv config unset-registry +``` + +This restores the default behavior of using ToolHive's built-in registry. + +## Organize servers with registry groups + +Registry groups allow you to organize related MCP servers and run them together +as a cohesive unit. This is particularly useful for creating team-specific +toolkits, workflow-based server collections, or environment-specific +configurations. + +:::note + +Registry groups are different from [runtime groups](./group-management.mdx). +Registry groups organize server definitions within registry files, while runtime +groups organize running server instances for access control. + +::: + +### Group structure + +Groups are defined as a top-level array in your custom registry: + +```json +{ + "servers": { + // Individual servers + }, + "groups": [ + { + "name": "group-name", + "description": "Description of what this group provides", + "servers": { + "server-name": { + // Complete server definition + } + }, + "remote_servers": { + "remote-server-name": { + // Complete remote server definition + } + } + } + ] +} +``` + +### Key characteristics + +- **Optional**: Groups are entirely optional. Omit the `groups` section if you + only need individual servers +- **Independent server definitions**: Each group contains complete server + configurations, not references to top-level servers +- **Self-contained**: Groups can define servers with the same names as top-level + servers but with different configurations +- **Flexible membership**: The same server can appear in multiple groups with + different configurations + +### Example: Multi-environment groups + +Here's an example showing how groups can organize servers for different +purposes: + +```json title='registry-with-groups.json' +{ + "$schema": "https://raw.githubusercontent.com/stacklok/toolhive-core/main/registry/types/data/toolhive-legacy-registry.schema.json", + "version": "1.0.0", + "last_updated": "2025-08-15T10:00:00Z", + "servers": { + "production-fetch": { + "description": "Production web content fetching server", + "image": "ghcr.io/stackloklabs/gofetch/server:latest", + "status": "Active", + "tier": "Community", + "transport": "streamable-http", + "permissions": { + "network": { + "outbound": { + "allow_host": [".company.com"], + "allow_port": [443] + } + } + } + } + }, + "groups": [ + { + "name": "devops-toolkit", + "description": "Complete DevOps toolkit for development teams", + "servers": { + "github": { + "description": "GitHub integration for repository and issue management", + "image": "ghcr.io/github/github-mcp-server:v0.15.0", + "status": "Active", + "tier": "Official", + "transport": "stdio", + "env_vars": [ + { + "name": "GITHUB_PERSONAL_ACCESS_TOKEN", + "description": "GitHub personal access token", + "required": true, + "secret": true + } + ] + }, + "heroku": { + "description": "Heroku platform deployment and management", + "image": "ghcr.io/stacklok/dockyard/npx/heroku-mcp-server:1.0.7", + "status": "Active", + "tier": "Community", + "transport": "stdio", + "env_vars": [ + { + "name": "HEROKU_API_KEY", + "description": "Heroku API key", + "required": true, + "secret": true + } + ] + } + } + }, + { + "name": "testing-suite", + "description": "Servers needed for automated testing workflows", + "servers": { + "test-fetch": { + "description": "Restricted fetch server for testing", + "image": "ghcr.io/stackloklabs/gofetch/server:latest", + "status": "Active", + "tier": "Community", + "transport": "streamable-http", + "args": ["--timeout", "5s"], + "permissions": { + "network": { + "outbound": { + "allow_host": ["test.example.com"], + "allow_port": [443] + } + } + } + } + } + } + ] +} +``` + +This registry provides: + +- A production-ready `production-fetch` server at the top level +- A `devops-toolkit` group with GitHub and Heroku servers for complete DevOps + workflows +- A `testing-suite` group with a restricted `test-fetch` server + +Notice how the `devops-toolkit` group contains multiple servers that work +together—GitHub for repository management and Heroku for +deployment—demonstrating how groups can bundle related functionality. + +### Run registry groups + +You can run entire groups using the group command: + +```bash +# Run all servers in the devops-toolkit group (GitHub + Heroku) +thv group run devops-toolkit + +# Run all servers in the testing-suite group +thv group run testing-suite + +# You can also pass environment variables and secrets to specific servers in the group +thv group run devops-toolkit --secret github-token,target=github.GITHUB_PERSONAL_ACCESS_TOKEN --secret heroku-key,target=heroku.HEROKU_API_KEY +``` + +Groups provide a convenient way to start multiple related servers with a single +command. + +## Next steps + +See [Run MCP servers](./run-mcp-servers.mdx) to run an MCP server from the +registry. + +Learn how to [create a custom MCP registry](../tutorials/custom-registry.mdx). + +## Related information + +- [`thv registry` command reference](../reference/cli/thv_registry.md) +- [`thv search` command reference](../reference/cli/thv_search.md) +- [`thv config set-registry` command reference](../reference/cli/thv_config_set-registry.md) +- [`thv config get-registry` command reference](../reference/cli/thv_config_get-registry.md) +- [`thv config unset-registry` command reference](../reference/cli/thv_config_unset-registry.md) diff --git a/versioned_docs/version-1.1/toolhive/guides-cli/run-mcp-servers.mdx b/versioned_docs/version-1.1/toolhive/guides-cli/run-mcp-servers.mdx new file mode 100644 index 00000000..bd3564e3 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-cli/run-mcp-servers.mdx @@ -0,0 +1,1032 @@ +--- +title: Run MCP servers +description: How to run MCP servers with the ToolHive CLI. +--- + +This guide explains how to run Model Context Protocol (MCP) servers using +ToolHive. It covers how to run servers from the ToolHive registry, customize +server settings, and run custom servers using Docker images or protocol schemes. + +## Run a server from the registry + +To run an MCP server from the [ToolHive registry](./registry.mdx), use the +[`thv run`](../reference/cli/thv_run.md) command with the name of the server you +want to run. The server name is the same as its name in the registry. + +```bash +thv run +``` + +The ToolHive registry contains both local containerized MCP servers and remote +MCP servers. ToolHive automatically handles the appropriate setup based on the +server type. + +### Local containerized servers + +For example, to run the `fetch` server, which is a local containerized MCP +server that fetches website contents: + +```bash +thv run fetch +``` + +### Remote MCP servers + +Remote MCP servers in the registry don't run as local containers but instead use +ToolHive's transparent HTTP proxy to forward requests to remote servers. For +example: + +```bash +thv run neon +thv run stripe +``` + +When you run a remote server from the registry, ToolHive uses the pre-configured +remote URL and authentication settings. + +:::note[Naming convention] + +Remote MCP servers use the `-remote` suffix **when they have a local +containerized counterpart** to distinguish between the two versions. For +example: + +- `notion-remote` indicates this is the remote version of a server that also has + a local `notion` version +- `neon` and `stripe` don't have local counterparts, so they don't use the + `-remote` suffix + +To run a remote notion mcp server, you should use the `notion-remote` name. + +```bash +thv run notion-remote +``` + +Use `thv search ` or `thv registry list` to discover available servers. + +::: + +### Run registry groups + +If you use a [custom registry](./registry.mdx#use-a-custom-registry) that +includes groups, you can run multiple related servers together as a unit. This +is useful when you need several servers for a specific workflow or project: + +```bash +thv group run +``` + +For example, to run all servers in a group called `dev-toolkit`: + +```bash +thv group run dev-toolkit +``` + +Running a group starts all servers defined within that group simultaneously, +saving you from running each server individually. See +[Registry groups](./registry.mdx#organize-servers-with-registry-groups) for more +information about organizing servers into groups in your custom registry. + +:::info[What's happening?] + +When you run an MCP server from the registry, ToolHive handles different server +types automatically: + +**For local containerized servers:** + +1. Pulls the image and launches a container using the configuration from the + registry. +2. Starts an HTTP proxy process on a random port to forward client requests to + the container. +3. Labels the container so it can be tracked by ToolHive: + ```yaml + toolhive: true + toolhive-name: + ``` + +**For remote MCP servers:** + +1. Uses the pre-configured remote URL from the registry. +2. Automatically detects if the remote server requires authentication. +3. Handles OAuth/OIDC authentication flows if needed. +4. Starts an HTTP proxy process on a random port to forward client requests to + the remote server. +5. Manages the server like any other ToolHive workload. No container is created + for remote MCP servers. + +::: + +See [Run a custom MCP server](#run-a-custom-mcp-server) to run a server that is +not in the registry, or [Run a remote MCP server](#run-a-remote-mcp-server) for +more details about remote server configuration. + +## Customize server settings + +You might need to customize the behavior of an MCP server, such as changing the +port, mounting a local directory, or passing secrets. ToolHive provides several +options to customize the server's configuration when you run it. + +For a complete list of options, run +[`thv run --help`](../reference/cli/thv_run.md) or see the +[`thv run` command reference](../reference/cli/thv_run.md). + +### Run a server with a custom name + +By default, the container name matches the MCP server's name in the registry or +is automatically generated from the image name when you run a custom server. To +give your server instance a custom name, use the `--name` option: + +```bash +thv run --name +``` + +For example: + +```bash +thv run --name my-fetch fetch +``` + +### Run a server with secrets + +Many MCP servers require secrets or other configuration variables to function +correctly. ToolHive lets you pass these secrets as environment variables when +starting the server. + +To pass a secret to an MCP server, use the `--secret` option: + +```bash +thv run --secret ,target= +``` + +The `target` parameter specifies the name of the environment variable in the MCP +server's container. This is useful for passing secrets like API tokens or other +sensitive information. + +For example: + +```bash +thv run --secret github,target=GITHUB_PERSONAL_ACCESS_TOKEN github +``` + +See [Secrets management](./secrets-management.mdx) to learn how to manage +secrets in ToolHive. + +### Run a server within a group + +To run an MCP server within a specific group, use the `--group` option. This +allows you to organize your servers and manage them collectively. + +```bash +thv run --group +``` + +:::note + +The group must exist before you can run a server in it. + +::: + +See [Organize servers into groups](./group-management.mdx) to learn more about +organizing servers and configuring client access. + +### Mount a local file or directory + +To enable file system access for an MCP server, you can either use the +`--volume` flag to mount specific paths or create a custom permission profile +that defines read and write permissions. + +See [File system access](./filesystem-access.mdx) for detailed examples. To +prevent sensitive files from being exposed when mounting a project, use +[.thvignore](./thvignore.mdx). + +### Restrict network access + +To restrict an MCP server's network access, use the `--isolate-network` flag. +This enforces network access rules from either the server's default registry +permissions or a custom permission profile you create. + +See [Network isolation](./network-isolation.mdx) for network architecture +details and examples. + +### Add command-line arguments + +Some MCP servers require additional arguments to run correctly. You can pass +these arguments after the server name in the +[`thv run`](../reference/cli/thv_run.md) command: + +```bash +thv run -- +``` + +For example: + +```bash +thv run my-mcp-server:latest -- --arg1 value1 --arg2 value2 +``` + +Check the MCP server's documentation for the required arguments. + +:::warning + +Some MCP servers in the ToolHive registry include default arguments that are +essential for proper operation. When you provide custom arguments using +`-- `, these replace the registry defaults entirely rather than adding to +them. + +Before adding custom arguments, check the server's registry entry: + +```bash +thv registry info --format json | jq '.args' +``` + +If default arguments are listed, include them along with your custom arguments +to ensure the server functions correctly. + +::: + +### Run a server on a specific port + +ToolHive creates a reverse proxy on a random port that forwards requests to the +container. This is the port that client applications connect to. To set a +specific proxy port instead, use the `--proxy-port` flag: + +```bash +thv run --proxy-port +``` + +### Run a server exposing only selected tools + +ToolHive can filter the tools returned to the client as result of a `tools/list` +command as well as block calls to tools that you don't want to expose. + +This can help reduce the amount of tools sent to the LLM while still using the +same MCP server, but it is not meant as a security feature. + +To filter the list of tools, use the `--tools` flag either once + +```bash +thv run --tools +``` + +Or multiple times + +```bash +thv run --tools --tools +``` + +For example: + +```bash +thv run --tools list_issues --tools get_issue github +``` + +If the server comes from the registry, ToolHive can validate the tool names +against the list advertised in the image reference. An error is returned in case +ToolHive cannot find one of the specified tools. + +### Override tool names and descriptions + +With ToolHive you can modify how tools exposed by an MCP server are exposed to +clients. In particular, tool names and descriptions can be changed. + +This is useful when you want to guide an agent toward calling a specific tool +for particular questions. + +One common use case is running multiple copies of the same MCP server with +different [network access levels](./network-isolation.mdx)—one for internal +resources and another for public internet access. By overriding the tool names +and descriptions, you can help your agent choose the right server for each task. + +For example, the `fetch` MCP server exposes a single `fetch` tool with a +description like: + +``` +"Fetches a URL from the internet and optionally extracts its contents as markdown." +``` + +To override this, create a configuration file with one entry under +`toolsOverride` for each tool you want to modify: + +```json +{ + "toolsOverride": { + "fetch": { + "name": "toolhive-docs-fetch", + "description": "Fetches a URL from https://docs.stacklok.com/toolhive website." + } + } +} +``` + +Then pass this file to [`thv run`](../reference/cli/thv_run.md): + +```sh +thv run --tools-override override.json fetch +``` + +The key in the override object is the _original tool name_, while the `name` +field contains the _new name_ that clients will see. + +:::info + +Take care when using `--tools` and `--tools-override` together in the same +command. + +Tool filtering and tool overrides work independently: filtering limits access to +a subset of tools, while overrides change how those tools appear to clients. + +When using both options, `--tools` must reference the _overridden names_ (the +new names you define) since those are what clients will see. + +::: + +## Run a custom MCP server + +To run an MCP server that isn't in the registry, you can use a +[Docker image](#run-a-server-from-a-docker-image) or a +[protocol scheme](#run-a-server-using-protocol-schemes) to dynamically build the +server. + +ToolHive supports the following transport methods: + +- **Standard I/O** (`stdio`), default:\ + ToolHive redirects SSE or Streamable HTTP traffic from the client to the + container's standard input and output. This acts as a secure proxy, ensuring + that the container doesn't have direct access to the network or the host + machine. + +- **HTTP with SSE (server-sent events)** (`sse`):\ + ToolHive creates a reverse proxy that forwards requests to the container using + the HTTP/SSE protocol. + +- **Streamable HTTP** (`streamable-http`):\ + ToolHive creates a reverse proxy that forwards requests to the container using + the Streamable HTTP protocol, which replaced SSE in the MCP specification as + of the `2025-03-26` revision. + +:::info + +As of ToolHive CLI version 0.6.0, the default proxy mode for `stdio` MCP servers +is `streamable-http`. + +For backward compatibility with the deprecated SSE transport, you can explicitly +set the transport to `sse` using the `--proxy-mode sse` flag when running the +server. + +::: + +### Run a server from a Docker image + +To run an MCP server from a Docker image, specify the image name and tag in the +[`thv run`](../reference/cli/thv_run.md) command. You can also specify a custom +name for the server instance, the transport method, and any additional arguments +required by the MCP server. + +```bash +thv run [--name ] [--transport ] -- +``` + +For example, to run an MCP server from a Docker image named +`my-mcp-server-image` that uses the Streamable HTTP transport method and takes +additional arguments: + +```bash +thv run --name my-mcp-server --transport streamable-http my-mcp-server-image:latest -- --arg1 value1 --arg2 value2 +``` + +Check your MCP server's documentation for the required arguments. + +:::info[What's happening?] + +When you run an MCP server from a Docker image, ToolHive: + +1. Pulls the image (`my-mcp-server-image:latest`) and launches a container with + the options and arguments you specified. +2. Launches an HTTP proxy on a random port (optionally, add + `--proxy-port ` to specify the port). +3. Labels the container so it can be tracked by ToolHive: + ```yaml + toolhive: true + toolhive-name: my-mcp-server + ``` +4. Sets up the specified `--transport` method (`stdio`, `sse`, or + `streamable-http`). + +::: + +See [`thv run --help`](../reference/cli/thv_run.md) for more options. + +### Run a server using protocol schemes + +ToolHive also supports running MCP servers directly from package managers. This +means you can launch MCP servers without building or publishing a Docker image, +and without installing language-specific build tools on your machine. + +Currently, three protocol schemes are supported: + +- `uvx://`: For Python-based MCP servers using the uv package manager +- `npx://`: For Node.js-based MCP servers using npm +- `go://`: For Go-based MCP servers + +```bash +thv run ://@ +``` + +You'll likely need to specify additional arguments like the transport method, +volumes, and environment variables. Check your MCP server's documentation and +see [`thv run --help`](../reference/cli/thv_run.md) for more options. + +:::info[What's happening?] + +When you use a protocol scheme, ToolHive: + +1. Detects the protocol scheme and extracts the package reference +2. Generates a Dockerfile based on the appropriate template +3. Builds a Docker image with the package installed +4. Runs the MCP server using the new image (see + [Run a server from a Docker image](#run-a-server-from-a-docker-image) for + details) + +::: + +To build the image without running it, see +[Build MCP containers](./build-containers.mdx). + +#### Examples + + + + +The `uvx://` protocol is used for Python-based MCP servers. The package name +must be a valid package in the [PyPI registry](https://pypi.org/). The +`@` suffix is _optional_ and defaults to the latest version if omitted. + +```bash +thv run --name aws-docs uvx://awslabs.aws-documentation-mcp-server@latest +``` + + + + +The `npx://` protocol is used for Node.js-based MCP servers. The package name +must be a valid package in the [npm registry](https://www.npmjs.com/). The +`@` suffix is _optional_ and defaults to the latest version if omitted. + +```bash +thv run --name pulumi npx://@pulumi/mcp-server@latest +``` + + + + +The `go://` protocol is used for Go-based MCP servers. The package name must be +a valid Go module repo URI referencing the `main` package. The `@` +suffix is **required**. + +```bash +thv run --name grafana go://github.com/grafana/mcp-grafana/cmd/mcp-grafana@latest +``` + +You can also run a local Go module by specifying the path to the module: + +```bash +# Run from a relative path +thv run go://./cmd/my-mcp-server + +# Run from the current directory +cd my-go-mcp-project +thv run go://. + +# Run from an absolute path +thv run go:///path/to/my-go-project +``` + + + + +### Configure network transport + +When you run custom MCP servers using the SSE (`--transport sse`) or Streamable +HTTP (`--transport streamable-http`) transport method, ToolHive automatically +selects a random port to expose from the container to the host and sets the +`MCP_PORT` and `FASTMCP_PORT` environment variables in the container. + +```mermaid +graph LR + A[MCP client] -->|HTTP request to
proxy port: 5432| B[ToolHive proxy
process
Port: 5432] + B -->|Forwards to
host port: 9876| C[MCP container
Host: 9876 → Container: 7654
ENV: MCP_PORT=7654] + C -->|Response| B + B -->|Response| A +``` + +This is equivalent to running a Docker container with +`docker run -p : ...` + +For MCP servers that use a specific port or don't recognize those environment +variables, specify the container port for ToolHive to expose using the +`--target-port` flag: + +```bash +thv run --transport streamable-http --target-port +``` + +ToolHive still maps the container port to a random port on the host to avoid +conflicts with commonly used ports. This is equivalent to running a Docker +container with `docker run -p : ...` + +```mermaid +graph LR + A[MCP client] -->|HTTP request to
proxy port: 5432| B[ToolHive proxy
process
Port: 5432] + B -->|Forwards to
host port: 9876| C["MCP container
(--target-port 3000)
Host: 9876 → Container: 3000
ENV: MCP_PORT=3000"] + C -->|Response| B + B -->|Response| A +``` + +Some MCP servers use command-line arguments to specify their transport and port. +For example, if your server expects the transport type as a positional argument +and requires the `--port` flag, you can pass it like this: + +```bash +thv run --transport streamable-http --target-port -- http --port +``` + +Check your MCP server's documentation for the required transport and port +configuration. + +### Add a custom CA certificate + +In corporate environments with TLS inspection or custom certificate authorities, +you may need to configure a CA certificate for ToolHive to use when building +containers from protocol schemes like `uvx://`, `npx://`, and `go://`. + +ToolHive provides both global configuration and per-command options for CA +certificates. + +#### Configure a global CA certificate + +To set a CA certificate that ToolHive will use for all container builds: + +```bash +thv config set-ca-cert /path/to/corporate-ca.crt +``` + +To view the currently configured CA certificate: + +```bash +thv config get-ca-cert +``` + +To remove the CA certificate configuration: + +```bash +thv config unset-ca-cert +``` + +#### Override CA certificate per command + +You can override the global CA certificate configuration for a specific run +using the `--ca-cert` flag: + +```bash +thv run --ca-cert /path/to/other-ca.crt uvx://some-package +``` + +This is useful when you need to use different CA certificates for different +servers or when testing with a specific certificate. + +#### Priority order + +ToolHive uses the following priority order for CA certificates: + +1. Command-line flag (`--ca-cert`) +2. Global configuration (`thv config set-ca-cert`) +3. No custom CA certificate (default behavior) + +For example: + +```bash +# Set a global CA certificate +thv config set-ca-cert /path/to/corporate-ca.crt + +# This uses the configured CA certificate +thv run uvx://some-package + +# This overrides the configured CA certificate +thv run --ca-cert /path/to/special-ca.crt uvx://other-package +``` + +## Run a remote MCP server + +You can run remote MCP servers directly by providing their URL. This allows you +to connect to MCP servers hosted elsewhere without needing to manage containers +locally. ToolHive creates a transparent proxy that handles authentication and +forwards requests to the remote server. + +### Basic remote server setup + +To run a remote MCP server, simply provide its URL: + +```bash +thv run [--name ] +``` + +For example: + +```bash +thv run https://api.example.com/mcp +``` + +If you don't specify a name with `--name`, ToolHive will automatically derive a +name from the URL by extracting the main domain name (e.g., `notion` from +`https://api.notion.com/mcp`). + +By default, remote servers use the `streamable-http` transport. If the server +uses Server-Sent Events (SSE), specify the transport flag: + +```bash +thv run https://api.example.com/sse --transport sse +``` + +:::info[What's happening?] + +When you run a remote MCP server, ToolHive: + +1. Automatically detects if the remote server requires authentication. +2. Handles OAuth/OIDC authentication flows if needed. +3. Starts an HTTP proxy process on a random port to forward client requests to + the remote server. +4. Manages the server like any other ToolHive workload. No container is created + for remote MCP servers. + +::: + +### Authentication setup + +Many remote MCP servers require authentication. ToolHive supports automatic +authentication detection and OAuth/OIDC flows. + +#### Auto-detect authentication + +ToolHive can automatically detect if a remote server requires authentication by +examining the server's response headers and status codes: + +```bash +thv run https://protected-api.com/mcp --name my-server +``` + +If authentication is required, ToolHive will prompt you to complete the OAuth +flow. When no client credentials are provided, ToolHive automatically registers +an OAuth client with the authorization server using RFC 7591 dynamic client +registration, eliminating the need to pre-configure client ID and secret. + +#### OIDC authentication + +For servers using OpenID Connect (OIDC), provide the issuer URL, client ID, and +client secret obtained from the application provider: + +```bash +thv run https://api.example.com/mcp \ + --name my-server \ + --remote-auth-issuer https://auth.example.com \ + --remote-auth-client-id my-client-id \ + --remote-auth-client-secret my-client-secret +``` + +#### OAuth2 authentication + +For servers using OAuth2, specify the authorization and token URLs along with +the client ID and secret obtained from the application provider: + +```bash +thv run https://api.example.com/mcp \ + --name my-server \ + --remote-auth-authorize-url https://auth.example.com/oauth/authorize \ + --remote-auth-token-url https://auth.example.com/oauth/token \ + --remote-auth-client-id my-client-id \ + --remote-auth-client-secret my-client-secret +``` + +#### Automatic token refresh + +ToolHive automatically handles OAuth token refresh for remote MCP servers. When +you authenticate with a remote server, ToolHive stores both the access token and +refresh token securely. The refresh token is used to automatically obtain new +access tokens when they expire, ensuring uninterrupted service without requiring +manual re-authentication. + +### Advanced remote server configuration + +#### OAuth scopes and parameters + +Specify custom OAuth scopes for the authentication flow: + +```bash +thv run https://api.example.com/mcp \ + ... \ + --remote-auth-scopes read,write,admin +``` + +#### Resource indicator (RFC 8707) + +When authenticating to remote MCP servers, you can specify a resource indicator +as defined by [RFC 8707](https://datatracker.ietf.org/doc/html/rfc8707). This +allows the authorization server to return an access token with a scoped +audience, which will then be passed to and validated by the remote MCP server. + +By default, ToolHive automatically uses the remote server URL as the resource +indicator when authenticating. The URL is validated, normalized (lowercase +scheme and host, fragments stripped), and included in the OAuth token request. + +To explicitly set a different resource indicator, use the +`--remote-auth-resource` flag: + +```bash +thv run https://api.example.com/mcp \ + ... \ + --remote-auth-resource https://api.example.com +``` + +The resource parameter must include a scheme and host, and cannot contain +fragments. If you provide an invalid resource parameter, ToolHive will return an +error. + +#### Custom authentication timeout + +Adjust the authentication timeout for slow networks: + +```bash +thv run https://api.example.com/mcp \ + ... \ + --remote-auth-timeout 2m +``` + +#### Skip browser authentication + +For headless environments, skip the browser-based OAuth flow: + +```bash +thv run https://api.example.com/mcp \ + ... \ + --remote-auth-skip-browser +``` + +#### Inject custom headers + +Some remote MCP servers require custom headers for tenant identification, API +keys, or other purposes. ToolHive can inject headers into every request +forwarded to the remote server. + +To add plaintext headers, use the `--remote-forward-headers` flag: + +```bash +thv run https://api.example.com/mcp \ + --name my-server \ + --remote-forward-headers "X-Tenant-ID=tenant123" \ + --remote-forward-headers "X-Custom-Header=value" +``` + +For sensitive values like API keys, use the `--remote-forward-headers-secret` +flag to reference values stored in ToolHive's secrets manager: + +```bash +# First, store the secret (enter the value when prompted) +thv secret set my-api-key + +# Then reference it by name +thv run https://api.example.com/mcp \ + --name my-server \ + --remote-forward-headers-secret "X-Api-Key=my-api-key" +``` + +You can combine plaintext and secret-backed headers in a single command: + +```bash +thv run https://api.example.com/mcp \ + --name my-server \ + --remote-forward-headers "X-Tenant-ID=tenant123" \ + --remote-forward-headers-secret "X-Api-Key=my-api-key" +``` + +:::warning[Security considerations] + +- Plaintext header values are stored in the server's configuration file. For + sensitive values (API keys, tokens), always use + `--remote-forward-headers-secret`. +- Secret-backed header values are resolved at runtime and never stored in + configuration files. +- Certain headers cannot be configured for security reasons, including `Host`, + `Connection`, `Transfer-Encoding`, and proxy-related headers like + `X-Forwarded-For`. + +::: + +### Remote server management + +Remote MCP servers are managed like any other ToolHive workload: + +- **List servers**: `thv list` shows remote servers with their target URLs +- **View logs**: `thv logs ` shows proxy and authentication logs +- **Stop/restart**: `thv stop ` and `thv restart ` +- **Remove**: `thv rm ` removes the proxy and configuration + +## Share and reuse server configurations + +ToolHive allows you to export a server's configuration and run servers using +previously exported configurations. This is useful for: + +- Sharing server setups with team members +- Creating backups of complex configurations +- Running identical server instances across different environments + +### Export a server configuration + +To export a running server's configuration to a file, use the +[`thv export`](../reference/cli/thv_export.md) command: + +```bash +thv export +``` + +For example, to export the configuration of a running server named "fetch": + +```bash +thv export fetch ./fetch-config.json +``` + +This creates a JSON file containing all the server's configuration, including: + +- Container image and version +- Environment variables and secrets references +- Volume mounts and permissions +- Network settings +- Transport configuration + +### Run a server from an exported configuration + +To run a server using a previously exported configuration, use the +[`thv run`](../reference/cli/thv_run.md) command with the `--from-config` flag: + +```bash +thv run --from-config +``` + +For example, to run a server using the exported configuration: + +```bash +thv run --from-config ./fetch-config.json +``` + +This creates a new server instance with identical settings to the original. If +the original server used secrets, you must have the same secrets available in +your ToolHive secrets store. + +:::note + +When you use `--from-config`, you cannot specify any other command-line flags. +The configuration file must contain all server settings. + +::: + +## Next steps + +- [Monitor and manage MCP servers](./manage-mcp-servers.mdx) to control your + running servers +- [Test your MCP servers](./test-mcp-servers.mdx) using the MCP Inspector or + `thv mcp` commands +- [Secure your servers](./auth.mdx) with OIDC authentication and Cedar policies +- [Enable telemetry and metrics](./telemetry-and-metrics.mdx) to gain + observability into MCP server interactions +- [Run the API server](./api-server.mdx) for programmatic access to ToolHive + +## Related information + +- [`thv run` command reference](../reference/cli/thv_run.md) +- [Client configuration](./client-configuration.mdx) +- [Secrets management](./secrets-management.mdx) +- [Custom permissions](./custom-permissions.mdx) + - [File system access](./filesystem-access.mdx) + - [Network isolation](./network-isolation.mdx) + +## Troubleshooting + +
+Server fails to start + +If a server fails to start: + +1. Check if Docker, Podman, or Colima is running +2. Verify you have internet access to pull images +3. Check if the port is already in use +4. Look at the error message for specific issues + +
+ +
+Server starts but isn't accessible + +If a server starts but isn't accessible: + +1. Check the server logs: + + ```bash + thv logs + ``` + +2. Verify the port isn't blocked by a firewall + +3. Make sure clients are properly configured (see + [Client configuration](./client-configuration.mdx)) + +
+ +
+Server crashes or exits unexpectedly + +If a server crashes or exits unexpectedly: + +1. List all MCP servers including stopped ones: + + ```bash + thv list --all + ``` + +2. Check the logs for error messages: + + ```bash + thv logs + ``` + +3. Check if the server requires any secrets or environment variables + +4. Verify the server's configuration and arguments + +
+ +
+Remote server authentication fails + +If a remote MCP server authentication fails: + +1. Check the server logs for authentication errors (see + [View server logs](./manage-mcp-servers.mdx#view-server-logs) for the correct + log file path on your platform) + +2. Verify the issuer URL is correct and accessible + +3. Check if the OAuth client ID and secret are valid + +4. Ensure the remote server supports the OAuth flow you're using + +5. Try re-authenticating by restarting the server: + + ```bash + thv restart + ``` + +
+ +
+Remote server connection issues + +If you can't connect to a remote MCP server: + +1. Verify the remote server URL is correct and accessible + +2. Check your network connectivity: + + ```bash + curl -I https://api.example.com/mcp + ``` + +3. Check if the remote server requires specific headers or authentication + +4. Review the server logs for connection errors: + + ```bash + thv logs + ``` + +5. Try running with debug mode for more detailed logs: + + ```bash + thv run --debug https://api.example.com/mcp --name my-server + ``` + +
+ +
+MCP server can't connect to services on the same host + +When ToolHive runs MCP servers in containers, they can't reach services on your +host machine using `localhost` due to container network isolation. + +Replace `localhost` in your MCP server configuration with the appropriate host +address for your platform: + +- **Docker Desktop (macOS/Windows)**: `host.docker.internal` +- **Podman Desktop**: `host.containers.internal` +- **Docker Engine (Linux)**: `172.17.0.1` (or your custom bridge gateway IP) + +For example, change `http://localhost:3000` to +`http://host.docker.internal:3000`. + +
diff --git a/versioned_docs/version-1.1/toolhive/guides-cli/secrets-management.mdx b/versioned_docs/version-1.1/toolhive/guides-cli/secrets-management.mdx new file mode 100644 index 00000000..1d27d90a --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-cli/secrets-management.mdx @@ -0,0 +1,369 @@ +--- +title: Secrets management +description: How to securely manage API tokens and other sensitive data in ToolHive. +--- + +MCP servers often need secrets like API tokens, connection strings, and other +sensitive parameters. ToolHive provides built-in secrets management features, +letting you manage these values securely without exposing them in plaintext +configuration files. + +## Secrets providers + +ToolHive supports multiple secrets providers to fit different security and +workflow requirements: + +- `encrypted` - ToolHive encrypts secrets using a password stored in your + operating system's keyring +- `1password` - ToolHive retrieves secrets from a 1Password vault + +You can use only one provider at a time. To select your preferred provider, run: + +```bash +thv secret setup +``` + +If you plan to use 1Password, first set up a 1Password service account and +obtain an API token. See the 1Password tab below for details. + + + + +When you select the `encrypted` provider, ToolHive prompts you to create an +encryption password that protects your secrets. + +ToolHive stores this encryption password in your operating system's keyring +(Keychain Access on macOS, Credential Manager on Windows, and dbus/Gnome Keyring +on Linux). This means you don't need to enter the password every time you use a +[`thv secret`](../reference/cli/thv_secret.md) command. + + + + +:::note + +The 1Password provider is read-only. You can list and view secrets, but you +can't create or delete them through ToolHive. Secrets must already exist in your +1Password vault. + +If you'd like to see write operations added, please +[open an issue](https://github.com/stacklok/toolhive/issues) or join the +`#toolhive-developers` channel in [Discord](https://discord.gg/stacklok). +Contributions are welcome! + +::: + +To use 1Password as your secrets provider, set up a 1Password service account. +For detailed instructions, see the +[1Password documentation](https://developer.1password.com/docs/service-accounts/get-started#create-a-service-account). + +Next, set the `OP_SERVICE_ACCOUNT_TOKEN` environment variable to your service +account's API token (displayed during the service account creation process). +This token is required for all [`thv secret`](../reference/cli/thv_secret.md) +commands: + +```bash +export OP_SERVICE_ACCOUNT_TOKEN= +``` + +Then, run `thv secret setup` and select `1password` when prompted. + +To reference a secret from 1Password, use the +[1Password secret reference](https://developer.1password.com/docs/cli/secret-reference-syntax) +URI format: + +```text +op:////[section-name/] +``` + +For example, to retrieve the `password` field from the `github` item in the +`MCPVault` vault: + +```bash +thv secret get op://MCPVault/github/password +``` + +Run [`thv secret list`](../reference/cli/thv_secret_list.md) to see all secrets +accessible to your service account, along with their URIs. + + + + +## Manage secrets + +### Create or update a secret + +The [`thv secret set`](../reference/cli/thv_secret_set.md) command lets you +create or update a secret in your secret store. You can set a secret +interactively by running: + +```bash +thv secret set +``` + +ToolHive prompts you to enter the secret value, and the input remains hidden for +security. + +Example: + +```bash +thv secret set github +# Enter your GitHub personal access token when prompted +``` + +Alternatively, you can set a secret using standard input: + +```bash +echo "MY_SECRET_VALUE" | thv secret set +``` + +:::tip[Example] + +Create a secret named `github` and set its value to your GitHub authentication +token using the GitHub CLI: + +```bash +gh auth token | thv secret set github +``` + +::: + +### List and view secrets + +To list the names of all secrets in your secret store without revealing their +values: + +```bash +thv secret list +``` + +To decrypt and view a secret's value: + +```bash +thv secret get +``` + +### Remove a secret + +To delete a secret when it's no longer needed: + +```bash +thv secret delete +``` + +### Reset your secret store + +ToolHive doesn't currently support changing the encryption password. If you need +to reset your secret store, delete the encrypted secrets file and recreate your +secrets. + +First, remove the encryption password from the keyring: + +```bash +thv secret reset-keyring +``` + +Then, delete the encrypted secrets file: + + + + + ```bash + rm ~/Library/Application\ Support/toolhive/secrets_encrypted + ``` + + + + + ```bash + rm ~/.config/toolhive/secrets_encrypted + ``` + + + + + ```powershell + Remove-Item "$env:LOCALAPPDATA\toolhive\secrets_encrypted" + ``` + + + + +The next time you run a `thv secret` command, ToolHive prompts you to create a +new encryption password and starts with a fresh secret store. + +## Use secrets with MCP servers + +ToolHive can securely pass secrets to an MCP server when you run it. This lets +the server access sensitive information without exposing it in plaintext. + +To do this, use the `--secret` flag with the +[`thv run`](../reference/cli/thv_run.md) command. The secret value is injected +into the container as an environment variable. + +```bash +thv run --secret ,target= +``` + +Check the MCP server's documentation to find the expected environment variable +names. For example, the GitHub MCP server expects the GitHub token to be passed +as `GITHUB_PERSONAL_ACCESS_TOKEN`. + +For MCP servers in the ToolHive registry, you can find the expected environment +variable names in the server's registry entry: + +```bash +thv registry info +``` + +### Example: GitHub API token + +This example shows how to set up a GitHub authentication token and use it with +the GitHub MCP server: + +1. Set the secret: + + ```bash + thv secret set github + # Enter your GitHub personal access token when prompted + ``` + +2. Run the GitHub MCP server with the token: + + ```bash + thv run --secret github,target=GITHUB_PERSONAL_ACCESS_TOKEN github + ``` + +The GitHub MCP server now has access to your GitHub token and can make +authenticated API requests. + +### Example: Multiple secrets + +You can provide multiple secrets to a server by using the `--secret` flag +multiple times: + +```bash +thv run \ + --secret github,target=GITHUB_TOKEN \ + --secret openai,target=OPENAI_API_KEY \ + multi-api-server +``` + +### Example: 1Password secret + +To use a secret from 1Password with an MCP server, set the +`OP_SERVICE_ACCOUNT_TOKEN` environment variable with your 1Password service +account API token and reference the secret using the `op://` URI format. + +```bash +OP_SERVICE_ACCOUNT_TOKEN= thv run \ + --secret op://MCPVault/slackbot/token,target=SLACK_BOT_TOKEN \ + --secret op://MCPVault/slackbot/team_id,target=SLACK_TEAM_ID \ + slack +``` + +This command retrieves the `token` and `team_id` fields from the `slackbot` item +in the `MCPVault` vault and passes them to the `slack` MCP server as the +`SLACK_BOT_TOKEN` and `SLACK_TEAM_ID` environment variables. + +## Next steps + +- [Configure your AI client](./client-configuration.mdx) to connect to your + servers +- [Set up custom permissions](./custom-permissions.mdx) to control filesystem + and network access + +## Related information + +- [`thv secret` command reference](../reference/cli/thv_secret.md) +- [Run MCP servers](./run-mcp-servers.mdx) + +## Troubleshooting + +
+Keyring access issues + +If you run into errors related to the system keyring: + +1. Make sure your system's keyring service is running +2. Check that you have the necessary permissions +3. On some Linux systems, you might need to install additional packages: + + ```bash + # For Debian/Ubuntu + sudo apt-get install gnome-keyring + + # For Fedora/RHEL + sudo dnf install gnome-keyring + ``` + +
+ +
+Secret not available to MCP server + +If your MCP server can't access a secret: + +1. Verify the secret exists: + + ```bash + thv secret list + ``` + +2. Verify the secret value: + + ```bash + thv secret get + ``` + +3. Check that you're using the correct secret name and target environment + variable. Inspect the MCP server's expected environment variables in the + registry: + + ```bash + thv registry info + ``` + +4. Inspect the server logs for any errors: + + ```bash + thv logs + ``` + +
+ +
+Forgot encryption password + +If the keyring entry is lost or corrupted and you forget your encryption +password, you won't be able to access your secrets. In this case, delete the +[encrypted secrets file](#reset-your-secret-store) and recreate your secrets. + +
+ +
+Issues accessing 1Password secrets + +If you can't access 1Password secrets: + +1. Verify the `OP_SERVICE_ACCOUNT_TOKEN` environment variable is set: + + ```bash + echo $OP_SERVICE_ACCOUNT_TOKEN + ``` + +2. Check that the token is valid and has the necessary permissions to access the + vault and item: + + ```bash + thv secret list + ``` + +3. Make sure the secret reference URI is correct and matches the vault, item, + and field names in 1Password: + + ```bash + thv secret get op:////[section-name/] + ``` + +
diff --git a/versioned_docs/version-1.1/toolhive/guides-cli/skills-management.mdx b/versioned_docs/version-1.1/toolhive/guides-cli/skills-management.mdx new file mode 100644 index 00000000..511d6c28 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-cli/skills-management.mdx @@ -0,0 +1,390 @@ +--- +title: Manage agent skills +sidebar_label: Agent skills +description: How to install, distribute, and manage agent skills with the ToolHive CLI. +--- + +[Agent skills](https://agentskills.io/) are reusable bundles of instructions, +scripts, and resources that teach AI agents how to perform specific tasks. While +MCP servers provide **tools** (the raw capabilities an agent can call), skills +provide the **knowledge** of when, why, and how to use those tools effectively. + +ToolHive lets you install skills from a +[ToolHive Registry Server](../guides-registry/skills.mdx) (by name), OCI +registries (by reference), or Git repositories (by URL), and manages their +lifecycle across multiple AI clients. + +:::tip[New to skills?] + +If you're not sure what skills are or how they relate to MCP servers, see +[Understanding skills](../concepts/skills.mdx) for a conceptual overview. + +::: + +## Prerequisites + +- The [ToolHive API server](./api-server.mdx) must be running. Start it in a + separate terminal window (the command blocks while running): + + ```bash + thv serve + ``` + + The server must remain running while you use `thv skill` commands. + + :::tip[Using the ToolHive desktop app?] + + If the ToolHive desktop app is already running, the API server is available + automatically. You can skip the `thv serve` step and use `thv skill` commands + directly. + + ::: + +- A supported AI client installed. See the + [client compatibility reference](../reference/client-compatibility.mdx) for + which clients support skills. + +## Install a skill + +You can install skills from a ToolHive Registry Server (by name), an OCI +registry (by reference), or a Git repository (by URL). + +### Install from the registry + +The simplest way to install a skill is by name. ToolHive looks up the skill in +the configured Registry Server and installs it automatically: + +```bash +thv skill install +``` + +For example: + +```bash +thv skill install toolhive-cli-user +``` + +### Install from an OCI registry + +To install a specific version of a skill from an OCI registry, provide the full +OCI reference: + +```bash +thv skill install ghcr.io//skills/: +``` + +### Install from a Git repository + +To install a skill directly from a Git repository: + +```bash +thv skill install git://github.com/anthropics/skills@main#skills/webapp-testing +``` + +The URL format is `git://host/owner/repo[@ref][#path/to/skill]`, where `@ref` is +a branch, tag, or commit hash, and `#path` points to the skill subdirectory. + +:::info[What's happening?] + +When you install a skill, ToolHive: + +1. Resolves the skill source (registry, OCI, or Git) +2. Downloads and extracts the skill files +3. Writes the `SKILL.md` and supporting files to your AI client's skills + directory +4. Records the installation in its database + +Your AI client discovers the skill automatically by reading from its skills +directory. + +::: + +### Overwrite an existing skill + +If a skill is already installed and you want to replace it, use the `--force` +flag: + +```bash +thv skill install my-skill --force +``` + +### Target a specific client + +If you have multiple supported clients, ToolHive installs the skill for the +first one it detects. To control which client receives the skill, use the +`--client` flag: + +```bash +thv skill install my-skill --client claude-code +``` + +See the [client compatibility reference](../reference/client-compatibility.mdx) +for the full list of clients that support skills. + +### Add a skill to a group + +You can organize skills into groups, just like MCP servers. Skills in a group +are automatically installed to clients registered with that group: + +```bash +thv skill install my-skill --group development +``` + +## Choose a scope + +Skills support two scopes that control where the skill files are installed: + +- **User scope** (default): Installs the skill globally for your user account. + The skill is available across all projects. +- **Project scope**: Installs the skill in a specific project directory. The + skill is only available when working in that project. + +### Install a user-scoped skill + +```bash +thv skill install my-skill +``` + +This installs the skill to your home directory (for example, +`~/.claude/skills/my-skill/` for Claude Code). + +### Install a project-scoped skill + +```bash +thv skill install my-skill --scope project --project-root /path/to/project +``` + +This installs the skill to the project directory (for example, +`/path/to/project/.claude/skills/my-skill/` for Claude Code). + +:::note + +The project root must be a Git repository. ToolHive validates this to prevent +installing skills in arbitrary directories. + +::: + +## List installed skills + +To see all installed skills: + +```bash +thv skill list +``` + +The output shows the name, version, scope, status, clients, and source reference +for each skill. + +### Filter the list + +You can filter skills by scope, client, or group: + +```bash +thv skill list --scope user +thv skill list --client claude-code +thv skill list --group development +``` + +### Get JSON output + +```bash +thv skill list --format json +``` + +## View skill details + +To see detailed information about a specific skill: + +```bash +thv skill info +``` + +This shows the skill's name, version, description, scope, status, source +reference, installation date, and associated clients. + +## Uninstall a skill + +To remove an installed skill: + +```bash +thv skill uninstall +``` + +For project-scoped skills, specify the scope and project root: + +```bash +thv skill uninstall my-skill --scope project --project-root /path/to/project +``` + +This removes the skill files from all associated client directories and deletes +the database record. + +## Create a skill + +Every skill requires a `SKILL.md` file at the root of its directory. At a +minimum, the file needs `name` and `description` fields in YAML frontmatter, +followed by Markdown instructions for the agent: + +```yaml title="my-skill/SKILL.md" +--- +name: my-skill +description: >- + What this skill does and when to use it. Include keywords that help agents + identify relevant tasks. +--- +# Instructions + +Step-by-step instructions for the agent... +``` + +The `name` must be lowercase alphanumeric with hyphens, must match the directory +name, and must not start or end with a hyphen. + +You can also add optional files in `scripts/`, `references/`, and `assets/` +subdirectories for executable code, documentation, and static resources. + +For the full list of frontmatter fields (version, license, allowed-tools, and +more) and directory structure details, see +[Understanding skills](../concepts/skills.mdx#skill-structure). + +## Build and publish skills + +Once you've created a skill, you can package it as an OCI artifact and publish +it to a registry for others to install. + +### Validate a skill + +Before building, validate your skill directory to check for errors: + +```bash +thv skill validate ./my-skill +``` + +This verifies that: + +- A `SKILL.md` file exists with valid frontmatter +- The `name` and `description` fields are present +- The `name` matches the directory name +- No symlinks or path traversal issues exist + +### Build an OCI artifact + +Package your skill into an OCI artifact: + +```bash +thv skill build ./my-skill +``` + +To specify a custom tag: + +```bash +thv skill build ./my-skill --tag ghcr.io/my-org/skills/my-skill:v1.0.0 +``` + +The build command stores the artifact in your local OCI store and prints the +reference. + +### Push to a registry + +After building, push the artifact to a remote OCI registry: + +```bash +thv skill push ghcr.io/my-org/skills/my-skill:v1.0.0 +``` + +:::note + +Pushing to a remote registry uses your existing container registry credentials +(for example, from `docker login` or `podman login`). Make sure you're +authenticated before pushing. + +::: + +## Next steps + +- [Configure your AI client](./client-configuration.mdx) to register clients + with ToolHive for automatic MCP server and skill configuration +- [Manage skills in the registry](../guides-registry/skills.mdx) to publish + skills for your team + +## Related information + +- [Understanding skills](../concepts/skills.mdx) for a conceptual overview +- [`thv skill` command reference](../reference/cli/thv_skill.md) +- [`thv serve` command reference](../reference/cli/thv_serve.md) +- [Agent Skills specification](https://agentskills.io/specification) + +## Troubleshooting + +
+Skill command fails with a connection error + +All skill commands require the ToolHive API server to be running. Start it with: + +```bash +thv serve +``` + +Then retry your skill command. + +
+ +
+Skill not discovered by your AI client + +If your AI client doesn't see an installed skill: + +1. Verify the skill is installed: + + ```bash + thv skill list + ``` + +2. Check that the skill was installed for the correct client: + + ```bash + thv skill info + ``` + +3. Verify the skill files exist in the expected directory. For Claude Code, + user-scoped skills are at `~/.claude/skills//` and project-scoped + skills are at `/.claude/skills//`. + +4. Restart your AI client to trigger skill discovery. + +
+ +
+Skill validation fails + +Run `thv skill validate` to see specific errors: + +```bash +thv skill validate ./my-skill +``` + +Common issues include: + +- Missing `SKILL.md` file in the directory +- Missing `name` or `description` in the frontmatter +- Skill `name` doesn't match the directory name +- Symlinks present in the skill directory (not allowed for security) + +
+ +
+Push to registry fails with authentication error + +Make sure you're authenticated with your container registry: + +```bash +# For GitHub Container Registry +echo $GITHUB_TOKEN | docker login ghcr.io -u USERNAME --password-stdin + +# For Docker Hub +docker login +``` + +Then retry the push command. + +
diff --git a/versioned_docs/version-1.1/toolhive/guides-cli/telemetry-and-metrics.mdx b/versioned_docs/version-1.1/toolhive/guides-cli/telemetry-and-metrics.mdx new file mode 100644 index 00000000..78ad3e52 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-cli/telemetry-and-metrics.mdx @@ -0,0 +1,466 @@ +--- +title: Telemetry and metrics +description: + How to enable OpenTelemetry and Prometheus instrumentation for ToolHive MCP + servers. +--- + +ToolHive includes built-in instrumentation using OpenTelemetry, which gives you +comprehensive observability for your MCP server interactions. You can export +traces and metrics to popular observability backends like Jaeger, Honeycomb, +Datadog, and Grafana Cloud, or expose Prometheus metrics locally. + +## What you can monitor + +ToolHive's telemetry captures detailed information about MCP interactions +including traces, metrics, and performance data. For a comprehensive overview of +the telemetry architecture, metrics collection, and monitoring capabilities, see +the [observability overview](../concepts/observability.mdx). + +## Enable telemetry + +You can enable telemetry when running an MCP server by using the OpenTelemetry +flags with the [`thv run`](../reference/cli/thv_run.md) command or configure +defaults for all MCP servers using the +[`thv config otel`](../reference/cli/thv_config_otel.md) commands. + +### Export to an OTLP endpoint + +You can export traces and metrics to an OpenTelemetry Protocol (OTLP) compatible +backend using the `--otel-endpoint` flag. You can also specify headers for +authentication and configure sampling rates. + +This example runs the Fetch MCP server and exports traces to a local instance of +the [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/): + +```bash +thv run \ + --otel-endpoint localhost:4318 \ + --otel-service-name fetch-mcp \ + --otel-insecure \ + fetch +``` + +:::note + +The `--otel-endpoint` is provided as a hostname and optional port, without a +scheme or path (e.g., use `api.honeycomb.io` or `api.honeycomb.io:443`, not +`https://api.honeycomb.io`). ToolHive automatically uses HTTPS unless +`--otel-insecure` is specified. + +::: + +By default, the service name is set to `thv-` (e.g., `thv-fetch`), +and the sampling rate is `0.1` (10%). You can customize these settings with +additional [configuration options](#configuration-options). + +:::tip[Recommendation] + +Set the `--otel-service-name` flag to a meaningful name for each MCP server. +This helps you identify the server in your observability backend. + +::: + +### Include environment variables + +You can include specific environment variables from your host system in +telemetry spans using the `--otel-env-vars` flag. This is useful for adding +context like deployment environment or service version to your traces. + +```bash +thv run \ + --otel-endpoint api.honeycomb.io \ + --otel-headers "x-honeycomb-team=" \ + --otel-env-vars "NODE_ENV,DEPLOYMENT_ENV,SERVICE_VERSION" \ + fetch +``` + +Only the environment variables you specify will be included in spans, and +they'll appear as attributes with the `environment.` prefix (e.g., +`environment.NODE_ENV`). + +### Add custom resource attributes + +You can add custom metadata to all telemetry signals (traces and metrics) using +custom resource attributes. These attributes are useful for adding context like +team ownership, deployment region, server type, or any other metadata that helps +you filter and query your telemetry data in observability platforms. + +Custom attributes are attached globally to all telemetry signals from the MCP +server, making them available for filtering, grouping, and searching in your +observability backend. + +#### Using the CLI flag + +Use the `--otel-custom-attributes` flag to specify custom attributes as +comma-separated key=value pairs: + +```bash +thv run \ + --otel-endpoint api.honeycomb.io \ + --otel-headers "x-honeycomb-team=" \ + --otel-custom-attributes "server_type=production,region=us-east-1,team=platform" \ + fetch +``` + +Attribute keys may contain letters, numbers, dots (`.`), underscores (`_`), and +hyphens (`-`). Spaces and most other punctuation are not allowed in keys. +Attribute values can contain any UTF-8 string, including URLs. For more details, +see the +[OpenTelemetry Resource Semantic Conventions](https://opentelemetry.io/docs/specs/semconv/resource/#attributes). + +#### Using environment variables + +You can also set custom attributes using the standard OpenTelemetry environment +variable `OTEL_RESOURCE_ATTRIBUTES`: + +```bash +export OTEL_RESOURCE_ATTRIBUTES="deployment.environment=staging,service.namespace=backend" +thv run --otel-endpoint api.honeycomb.io fetch +``` + +#### Combining both methods + +When using both the CLI flag and environment variable, attributes from both +sources are combined: + +```bash +export OTEL_RESOURCE_ATTRIBUTES="deployment.environment=production" +thv run \ + --otel-endpoint api.honeycomb.io \ + --otel-custom-attributes "region=us-west-2,team=infrastructure" \ + fetch +``` + +This will add all three attributes to your telemetry data: + +- `deployment.environment=production` (from environment variable) +- `region=us-west-2` (from CLI flag) +- `team=infrastructure` (from CLI flag) + +#### Common use cases + +Custom attributes are particularly useful for: + +- **Critical context**: Add critical context to telemetry (e.g., service, host, + or environment) so data is meaningful and traceable. +- **Fast filtering and troubleshooting**: Enable fast filtering, grouping, and + correlation across distributed components during troubleshooting. +- **Consistent metadata:** Support standardized metadata via OpenTelemetry + semantic conventions for reliable observability across systems. +- **Root-cause analysis:** Pinpoint which resource is responsible for + performance issues. +- **Telemetry enrichment:** Enable automatic or manual enrichment using resource + detectors and the OpenTelemetry Collector. + +:::tip + +Choose attribute names that follow OpenTelemetry semantic conventions when +possible. For example, use `service.version` instead of `app_version` for better +compatibility with observability tools. + +::: + +### Enable Prometheus metrics + +You can expose Prometheus-style metrics at `/metrics` on the main transport port +for local scraping using the `--otel-enable-prometheus-metrics-path` flag. + +This example runs the Fetch MCP server and enables the Prometheus metrics +endpoint: + +```bash +thv run --otel-enable-prometheus-metrics-path fetch +``` + +To access the metrics, you can use `curl` or any Prometheus-compatible scraper. +The metrics are available at `http://127.0.0.1:/metrics`, where `` +is the port assigned to the MCP server. + +```bash +# Get the port number assigned to the MCP server +thv list + +# Replace with the actual port number from the output of `thv list` +curl http://127.0.0.1:/metrics +``` + +### Dual export + +You can export to both an OTLP endpoint and expose Prometheus metrics +simultaneously. + +This example exports to Honeycomb and enables the Prometheus metrics endpoint: + +```bash +thv run \ + --otel-endpoint api.honeycomb.io \ + --otel-headers "x-honeycomb-team=" \ + --otel-enable-prometheus-metrics-path \ + fetch +``` + +## Configuration options + +You can configure telemetry settings globally or per MCP server. The global +configuration is stored in the ToolHive configuration file, while per-server +configuration can be specified using command-line flags when running an MCP +server. + +### Per-server configuration + +The table below lists the available configuration options for enabling telemetry +when running an MCP server with the `thv run` command: + +```bash +thv run [--otel-endpoint ] [--otel-service-name ] \ + [--otel-metrics-enabled=] [--otel-tracing-enabled=] \ + [--otel-sampling-rate ] [--otel-headers ] \ + [--otel-custom-attributes ] [--otel-env-vars ] \ + [--otel-insecure] [--otel-enable-prometheus-metrics-path] \ + [--otel-use-legacy-attributes=] \ + +``` + +| Flag | Description | Default | +| --------------------------------------- | ------------------------------------------------------------------- | -------------- | +| `--otel-endpoint` | OTLP endpoint (e.g., `api.honeycomb.io`) | None | +| `--otel-metrics-enabled` | Enable OTLP metrics export (when OTLP endpoint is configured) | `true` | +| `--otel-tracing-enabled` | Enable distributed tracing (when OTLP endpoint is configured) | `true` | +| `--otel-service-name` | Service name for telemetry | `thv-` | +| `--otel-sampling-rate` | Trace sampling rate (0.0-1.0) | `0.1` (10%) | +| `--otel-headers` | Authentication headers in `key=value` format | None | +| `--otel-custom-attributes` | Custom resource attributes in `key=value` format | None | +| `--otel-env-vars` | List of environment variables to include in telemetry spans | None | +| `--otel-insecure` | Connect using HTTP instead of HTTPS | `false` | +| `--otel-enable-prometheus-metrics-path` | Enable `/metrics` endpoint | `false` | +| `--otel-use-legacy-attributes` | Emit legacy attribute names alongside new OTel semantic conventions | `true` | + +### Global configuration + +You can set default telemetry options for all MCP servers using the +[`thv config otel`](../reference/cli/thv_config_otel.md) command. This way you +don't have to set the same flags every time you run an MCP server. + +These defaults are applied to all MCP servers unless overridden by command-line +flags when you run a specific server. + +Currently the OpenTelemetry endpoint, sampling rate, and environment variables +can be set globally. For example, to set the default OTLP endpoint and sampling +rate: + +```bash +thv config otel set-endpoint api.honeycomb.io +thv config otel set-metrics-enabled true +thv config otel set-tracing-enabled true +thv config otel set-sampling-rate 0.25 +thv config otel set-enable-prometheus-metrics-path true +thv config otel set-insecure true +``` + +Each command has a corresponding `get` and `unset` command to retrieve or remove +the configuration. For example, to check the current OTLP endpoint: + +```bash +thv config otel get-endpoint +``` + +## Observability backends + +ToolHive can export telemetry data to many different observability backends. It +supports exporting traces and metrics to any backend that implements the OTLP +protocol. Some common examples are listed below, but specific configurations +will vary based on your environment and requirements. + +### OpenTelemetry Collector (recommended) + +The OpenTelemetry Collector is a vendor-agnostic way to receive, process and +export telemetry data. It supports many backend services, scalable deployment +options, and advanced processing capabilities. + +```mermaid +graph LR + A[ToolHive] -->|traces & metrics| B[OpenTelemetry Collector] + B --> C[AWS CloudWatch] + B --> D[Splunk] + B --> E[New Relic] + B --> F[Other OTLP backends] +``` + +You can run the OpenTelemetry Collector locally or use a managed service. For +local deployment, follow the +[OpenTelemetry Collector documentation](https://opentelemetry.io/docs/collector/). + +To export data to a local OpenTelemetry Collector, set your OTLP endpoint to the +OTLP http receiver port (default is `4318`): + +```bash +thv run \ + --otel-endpoint localhost:4318 \ + --otel-insecure \ + fetch +``` + +### Prometheus + +To collect metrics using Prometheus, run your MCP server with the +`--otel-enable-prometheus-metrics-path` flag and add the following to your +Prometheus configuration: + +```yaml title="prometheus.yml" +scrape_configs: + - job_name: 'toolhive-mcp-proxy' + static_configs: + - targets: ['localhost:'] + scrape_interval: 15s + metrics_path: /metrics +``` + +You can add multiple MCP servers to the `targets` list. Replace +`` with the port number assigned to each MCP server. + +### Jaeger + +Jaeger is a popular open-source distributed tracing system. You can run it +locally or use a managed service within your enterprise or from a third-party +provider. + +Follow the +[Jaeger getting started guide](https://www.jaegertracing.io/docs/latest/getting-started/#all-in-one) +to set up a local Jaeger instance. Once running, you can export traces to Jaeger +by setting the OTLP endpoint to Jaeger's collector: + +```bash +thv run \ + --otel-endpoint localhost:4318 \ + --otel-metrics-enabled=false \ + --otel-insecure \ + +``` + +Access the Jaeger UI at `http://localhost:16686` to view traces. + +### Honeycomb + +You can send OpenTelemetry data directly to +[Honeycomb's OTLP endpoint](https://docs.honeycomb.io/send-data/opentelemetry/#using-the-honeycomb-opentelemetry-endpoint), +or use the [OpenTelemetry Collector](#opentelemetry-collector-recommended) to +forward data to Honeycomb. This example sends data directly to Honeycomb: + +```bash +thv run \ + --otel-endpoint api.honeycomb.io \ + --otel-headers "x-honeycomb-team=" \ + --otel-service-name production-mcp-proxy \ + +``` + +You'll need your Honeycomb API key, which you can find in your +[Honeycomb account settings](https://ui.honeycomb.io/account). + +### Datadog + +Datadog has [multiple options](https://docs.datadoghq.com/opentelemetry/) for +collecting OpenTelemetry data: + +- The + [**OpenTelemetry Collector**](https://docs.datadoghq.com/opentelemetry/setup/collector_exporter/) + is recommended for existing OpenTelemetry users or users wanting a + vendor-neutral solution. + +- The [**Datadog Agent**](https://docs.datadoghq.com/opentelemetry/setup/agent) + is recommended for existing Datadog users. + +### Grafana Cloud + +You can send OpenTelemetry data to Grafana Cloud using +[Grafana Alloy](https://grafana.com/docs/opentelemetry/collector/grafana-alloy/), +Grafana Labs' supported distribution of the OpenTelemetry Collector. This is the +recommended method for production deployments. + +You can also send data directly to +[Grafana Cloud's OTLP endpoint](https://grafana.com/docs/grafana-cloud/send-data/otlp/send-data-otlp/#manual-opentelemetry-setup-for-advanced-users): + +```bash +thv run \ + --otel-endpoint otlp-gateway-prod-us-central-0.grafana.net \ + --otel-headers "Authorization=Basic $(echo -n 'user:password' | base64)" \ + +``` + +## Performance considerations + +### Sampling rates + +Adjust sampling rates based on your environment: + +- **Development**: `--otel-sampling-rate 1.0` (100% sampling) +- **Production**: `--otel-sampling-rate 0.01` (1% sampling for high-traffic + systems) +- **Default**: `--otel-sampling-rate 0.1` (10% sampling) + +### Network overhead + +Telemetry adds minimal overhead when properly configured: + +- Use appropriate sampling rates for your traffic volume +- Monitor your observability backend costs and adjust sampling accordingly + +## Next steps + +- [Learn about ToolHive's observability model](../concepts/observability.mdx) + for a deeper understanding of the telemetry architecture +- [Monitor and manage MCP servers](./manage-mcp-servers.mdx) for server + lifecycle management including logs and status monitoring +- [Collect telemetry for MCP workloads](../integrations/opentelemetry.mdx) for a + hands-on tutorial with an OpenTelemetry Collector + +## Related information + +- Tutorial: + [Collect telemetry for MCP workloads](../integrations/opentelemetry.mdx) +- [Telemetry and monitoring concepts](../concepts/observability.mdx) +- [`thv run` command reference](../reference/cli/thv_run.md) +- [Run MCP servers](run-mcp-servers.mdx) + +## Troubleshooting + +
+Traces not received by collector + +If traces aren't showing up in your backend: + +1. Verify the endpoint URL and authentication headers +2. Check network connectivity to the OTLP endpoint +3. Ensure sampling rate isn't too low (set to `1.0` temporarily for testing) +4. Check the ToolHive log file for errors related to telemetry (the log file + path is displayed when you start a server with `thv run`) +5. If your endpoint uses a self-signed certificate or a certificate from a + custom CA, use the `--thv-ca-bundle` flag to add your CA or self-signed + certificate. + +
+ +
+Prometheus metrics not available + +If the `/metrics` endpoint isn't accessible: + +1. Confirm `--otel-enable-prometheus-metrics-path` is set +2. Check that you're accessing the correct port +3. Verify no firewall is blocking the port + +
+ +
+High CPU or memory usage + +If telemetry is consuming too many resources on your system: + +1. Reduce the sampling rate with `--otel-sampling-rate` for specific servers or + globally with `thv config otel set-sampling-rate` +2. Only enable telemetry for servers you need to monitor +3. Monitor the resource usage of the OpenTelemetry Collector or other backend + services + +
diff --git a/versioned_docs/version-1.1/toolhive/guides-cli/test-mcp-servers.mdx b/versioned_docs/version-1.1/toolhive/guides-cli/test-mcp-servers.mdx new file mode 100644 index 00000000..602ea931 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-cli/test-mcp-servers.mdx @@ -0,0 +1,103 @@ +--- +title: Test MCP servers +description: Learn how to test and validate MCP servers using the ToolHive CLI. +--- + +ToolHive includes several commands to help you test and validate MCP servers. +These commands ensure that your servers are functioning correctly. This is +useful for: + +- Validating new MCP server installations +- Troubleshooting existing MCP servers +- Testing custom MCP servers during development + +## MCP Inspector + +The [MCP Inspector](https://modelcontextprotocol.io/docs/tools/inspector) is an +interactive tool for testing and debugging MCP servers. + +You can quickly launch the MCP Inspector and connect it to an MCP server running +with ToolHive using the `thv inspector` command: + +```bash +thv inspector +``` + +ToolHive downloads and runs the official Inspector container image and outputs +the URL to open it in your web browser with the target MCP server's details +pre-populated. + +Example: + +```bash +thv run fetch +thv inspector fetch +``` + +The output will look like this: + +```text +2:52PM INFO Pulling image: ghcr.io/modelcontextprotocol/inspector:0.17.0 +2:52PM INFO Pull complete for image: ghcr.io/modelcontextprotocol/inspector:0.17.0 +2:52PM INFO Waiting for MCP Inspector to be ready... +2:52PM INFO Connected to MCP server: fetch +2:52PM INFO Inspector UI is now available at http://localhost:6274?transport=streamable-http&serverUrl=http://host.docker.internal:21309/mcp&MCP_PROXY_AUTH_TOKEN= +``` + +Open the provided URL in your web browser to access the MCP Inspector UI, where +you can interactively test the MCP server's tools, prompts, and resources. + +## `thv mcp` commands + +ToolHive also includes several +[`thv mcp` commands](../reference/cli/thv_mcp_list.md) to test and validate MCP +servers. You can list the MCP server's tools, prompts, and resources: + +```bash +thv mcp list tools --server +thv mcp list prompts --server +thv mcp list resources --server +``` + +Replace `` with either the MCP server name or its ToolHive +proxy URL. You can find both values in the output of `thv list`. + +Add the `--format json` flag to get the output in JSON format. + +:::note + +Currently, the `thv mcp` commands only support using server names for local MCP +servers. For remote servers, you must use the full URL instead of the server +name. You can track +[this issue on GitHub](https://github.com/stacklok/toolhive/issues/2035). + +To use URLs instead of names, get the server URL from `thv list` and use it +directly, like `thv mcp list tools --server http://localhost:12345/mcp`. + +::: + +## ToolHive playground + +While the MCP Inspector and `thv mcp` commands are great for basic functional +testing and validating spec compliance, you may want to experiment with MCP +servers in a more interactive way. It's also important to see how MCP servers +perform in real-world scenarios with actual AI models. + +The _playground_ in the ToolHive UI lets you test different tools interactively +and see how your MCP server responds to various prompts. + +See [Test MCP servers in the ToolHive UI](../guides-ui/playground.mdx) to learn +about the playground's features and how to get started. + +## Next steps + +- [Build MCP containers](./build-containers.mdx) to package your tested servers + for deployment +- [Set up CI/CD patterns](./advanced-cicd.mdx) to automate testing in your + pipeline +- [Secure your servers](./auth.mdx) with authentication and authorization + +## Related information + +- [`thv inspector` command reference](../reference/cli/thv_inspector.md) +- [`thv mcp list` command reference](../reference/cli/thv_mcp_list.md) diff --git a/versioned_docs/version-1.1/toolhive/guides-cli/thvignore.mdx b/versioned_docs/version-1.1/toolhive/guides-cli/thvignore.mdx new file mode 100644 index 00000000..37fe16a3 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-cli/thvignore.mdx @@ -0,0 +1,141 @@ +--- +title: Hide sensitive files +description: + Use .thvignore to prevent secrets from leaking into MCP containers while + keeping fast bind mounts for development. +--- + +Some MCP servers need access to your project files, but you don't want to expose +secrets like `.env`, SSH keys, or cloud credentials. ToolHive supports a +`.thvignore` mechanism that masks selected paths from the container while +keeping all other files available through a live bind mount for a smooth +developer experience. + +## How it works + +When you mount a directory and a `.thvignore` file is present at the mount +source, ToolHive resolves the ignore patterns and overlays those paths inside +the container: + +- Directories (for example, `.ssh/`, `node_modules/`): overlaid using a tmpfs + mount at the container path +- Files (for example, `.env`, `secrets.json`): overlaid using a bind mount of a + shared, empty host file at the container path + +The rest of the files remain bind-mounted from your host, so edits are visible +in the container immediately. + +## Create an ignore file + +Create a file named `.thvignore` at the root of the directory you intend to +mount. Use simple, gitignore-like patterns: + +```text +# secrets +.env +.env.* +*.key +*.pem + +# cloud credentials +.aws/ +.gcp/ + +# SSH keys +.ssh/ + +# OS junk +.DS_Store +``` + +Guidelines: + +- `dir/` matches a directory directly under the mount source +- `file.ext` matches a file directly under the mount source +- `*.ext` matches any file with that extension directly under the mount source +- Lines starting with `#` are comments; blank lines are ignored + +:::info[Pattern matching] + +ToolHive uses simple gitignore-like patterns. Advanced gitignore glob syntax +like `**/*.env` (to match files in any subdirectory) is not currently supported. +Patterns only match files and directories directly under the mount source. + +::: + +## Run a server with .thvignore + +Mount your project directory as usual. ToolHive automatically reads `.thvignore` +if present: + +```bash +thv run --volume ./my-project:/projects filesystem +``` + +To print resolved overlay targets for debugging: + +```bash +thv run --volume ./my-project:/projects \ + --print-resolved-overlays \ + filesystem +``` + +The resolved overlays are logged to the workload's log file. For a complete list +of options, see the [`thv run` command reference](../reference/cli/thv_run.md). + +## Global ignore patterns + +You can define global ignore patterns in a platform-specific location: + +- **Linux**: `~/.config/toolhive/thvignore` +- **macOS**: `~/Library/Application Support/toolhive/thvignore` +- **Windows**: `%LOCALAPPDATA%\toolhive\thvignore` + +Patterns contained in the global configuration are loaded in addition to a local +`.thvignore` file. To disable global patterns for a specific workload, use the +`--ignore-globally=false` option: + +```bash +thv run --ignore-globally=false --volume ./my-project:/projects filesystem +``` + +:::tip[Recommendation] + +Set machine-wide patterns (for example, `.aws/`, `.gcp/`, `.ssh/`, `*.pem`, +`.docker/config.json`) in the global file, and keep app-specific patterns (for +example, `.env*`, build artifacts) in your project's local `.thvignore`. + +::: + +## Troubleshooting + +
+Overlays didn't apply + +- Ensure `.thvignore` exists in the mount source directory (not elsewhere) +- Confirm patterns match actual names relative to the mount source +- Run with `--print-resolved-overlays` and check the workload's log file path + displayed by `thv run` + +
+ +
+Can't list a parent directory + +- On SELinux systems, listing a parent directory may fail even though specific + files are accessible. Probe individual paths instead (for example, `stat` or + `cat`). + +
+ +## Next steps + +- [Restrict network access](./network-isolation.mdx) to control outbound + connections from MCP servers + +## Related information + +- [File system access](./filesystem-access.mdx) +- [Run MCP servers](./run-mcp-servers.mdx) +- [Network isolation](./network-isolation.mdx) +- [`thv run` command reference](../reference/cli/thv_run.md) diff --git a/versioned_docs/version-1.1/toolhive/guides-cli/token-exchange.mdx b/versioned_docs/version-1.1/toolhive/guides-cli/token-exchange.mdx new file mode 100644 index 00000000..cd9d7cba --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-cli/token-exchange.mdx @@ -0,0 +1,216 @@ +--- +title: Configure token exchange for backend authentication +description: + How to set up token exchange so MCP servers can authenticate to backend + services using ToolHive. +--- + +This guide shows you how to configure token exchange, which allows MCP servers +to authenticate to backend APIs using short-lived, properly scoped tokens +instead of embedded secrets. + +For conceptual background on how token exchange works and its security benefits, +see [Backend authentication](../concepts/backend-auth.mdx), which includes a +[sequence diagram](../concepts/backend-auth.mdx#same-idp-with-token-exchange) +illustrating the complete flow. + +## Prerequisites + +Before you begin, make sure you have: + +- ToolHive installed and working +- Basic familiarity with OAuth, OIDC, and JWT concepts +- An identity provider that supports + [RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) token exchange (such + as Okta, Auth0, or Keycloak) +- A backend service configured to accept tokens from your identity provider +- Familiarity with [Authentication and authorization](./auth.mdx) setup in + ToolHive + +From your identity provider, you'll need: + +- Audience value for the MCP server +- Issuer URL +- JWKS URL (for key verification) +- Token exchange endpoint URL +- Client credentials for the token exchange client + +## Configure your identity provider + +Token exchange requires your identity provider to issue tokens for the backend +service when presented with a valid MCP server token. The exact configuration +steps vary by provider, but generally include: + +### Register a token exchange client + +Create an OAuth application in your identity provider for ToolHive to use when +performing token exchange: + +- Note the client ID and client secret +- Grant the application permission to use the + `urn:ietf:params:oauth:grant-type:token-exchange` grant type + +Token exchange is an authenticated flow—ToolHive uses these credentials to prove +its identity when requesting exchanged tokens from the identity provider. + +:::tip[Okta] + +Create an API Services application for ToolHive and enable the token exchange +grant type in the application settings. + +::: + +### Define audience and scopes for the backend service + +Configure your identity provider to recognize the backend service: + +- Define the audience value that identifies your backend service (for example, + `backend-api`) +- Specify the scopes the backend service accepts (for example, `api:read`, + `api:write`) + +:::tip[Okta] + +Create a custom authorization server for the backend service and define the +scopes under **Security > API > Authorization Servers**. + +::: + +### Create an access policy + +Set up a policy that permits token exchange and controls what scopes are +included in exchanged tokens: + +- Enable the token exchange grant type for the ToolHive client +- Define which users or groups can obtain tokens for the backend service +- Specify the scopes included in exchanged tokens + +:::tip[Okta] + +Add a trust relationship from the MCP authorization server to the backend +authorization server, then create access policies on the backend server to +permit token exchange. + +Consult the +[Okta token exchange documentation](https://developer.okta.com/docs/guides/set-up-token-exchange/main/) +for detailed steps. + +::: + +## MCP server requirements + +The MCP server that ToolHive fronts must accept a per-request authentication +token. Specifically, the server should: + +- Read the access token from the `Authorization: Bearer` header (or a custom + header if you configure `--token-exchange-header-name`) +- Use this token to authenticate to the backend service +- Not rely on hardcoded secrets or environment variables for backend + authentication + +ToolHive injects the exchanged token into each request, so the MCP server +receives a fresh, properly scoped token for every call. + +:::tip[Example] + +The [Apollo MCP server](https://github.com/apollographql/apollo-mcp-server) +supports token passthrough via its config +(`disable_auth_token_passthrough: false`). + +::: + +## Run an MCP server with token exchange + +Once your identity provider is configured, start your MCP server with token +exchange enabled: + +```bash +thv run \ + --oidc-audience \ + --oidc-issuer \ + --oidc-jwks-url \ + --token-exchange-url \ + --token-exchange-client-id \ + --token-exchange-client-secret \ + --token-exchange-audience \ + --token-exchange-scopes \ + +``` + +### Parameter reference + +| Parameter | Description | +| -------------------------------- | ------------------------------------------------------------------------- | +| `--oidc-*` | Standard OIDC parameters for validating incoming client tokens | +| `--token-exchange-url` | Your identity provider's token exchange endpoint | +| `--token-exchange-client-id` | Client ID for ToolHive to authenticate to the IdP | +| `--token-exchange-client-secret` | Client secret for ToolHive (or use `--token-exchange-client-secret-file`) | +| `--token-exchange-audience` | Target audience for the exchanged token (your backend service) | +| `--token-exchange-scopes` | Scopes to request for the backend service | + +Optional parameters: + +- `--token-exchange-header-name`: Custom header name for injecting the exchanged + token (defaults to replacing the `Authorization` header) +- `--token-exchange-subject-token-type`: Type of subject token to exchange; use + `id_token` for Google STS (defaults to `access_token`) + +## Verify the configuration + +To confirm token exchange is working: + +1. Connect to the MCP server with a client that supports authentication +2. Make a tool call that requires backend access +3. Check that the request succeeds and the backend receives a properly scoped + token + +You can also verify by examining your identity provider's logs for successful +token exchange requests, or by checking audit logs on your backend service to +confirm requests arrive with the correct user identity and scopes. + +## Example: Okta configuration + +This example shows a complete configuration for an MCP server that connects to a +backend API, using Okta for token exchange: + +```bash +thv run \ + --oidc-audience mcp-server \ + --oidc-issuer https://dev-123456.okta.com/oauth2/aus1234567890 \ + --oidc-jwks-url https://dev-123456.okta.com/oauth2/aus1234567890/v1/keys \ + --token-exchange-url https://dev-123456.okta.com/oauth2/aus9876543210/v1/token \ + --token-exchange-client-id 0oa0987654321fedcba \ + --token-exchange-client-secret-file /path/to/client-secret \ + --token-exchange-audience backend-api \ + --token-exchange-scopes "api:read,api:write" \ + +``` + +Key points in this example: + +- **Two authorization servers**: The `--oidc-issuer` URL (`aus1234567890`) is + the MCP authorization server that validates incoming client tokens. The + `--token-exchange-url` uses a different authorization server (`aus9876543210`) + that issues tokens for the backend API. +- **Audience transformation**: Client tokens arrive with audience `mcp-server`. + ToolHive exchanges them for tokens with audience `backend-api`, which the + backend service expects. +- **Scope transformation**: The original token has MCP-specific scopes (like + `mcp:tools:call`), while the exchanged token has backend-specific scopes + (`api:read`, `api:write`). The user's identity is preserved, but the + permissions are transformed for the target service. + +## Next steps + +- [Monitor server activity](./telemetry-and-metrics.mdx) with OpenTelemetry and + Prometheus +- [Test your MCP servers](./test-mcp-servers.mdx) to validate your configuration + +## Related information + +- [Backend authentication](../concepts/backend-auth.mdx) - conceptual overview + of token exchange and federation +- [Authentication and authorization](./auth.mdx) - basic auth setup for MCP + servers +- [CLI reference for `thv run`](../reference/cli/thv_run.md) - complete list of + flags diff --git a/versioned_docs/version-1.1/toolhive/guides-k8s/auth-k8s.mdx b/versioned_docs/version-1.1/toolhive/guides-k8s/auth-k8s.mdx new file mode 100644 index 00000000..d836d94f --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-k8s/auth-k8s.mdx @@ -0,0 +1,1017 @@ +--- +title: Authentication and authorization +description: + How to set up authentication and authorization for MCP servers in Kubernetes + using the ToolHive Operator. +--- + +import OidcPrerequisites from '../_partials/_oidc-prerequisites.mdx'; +import BasicCedarConfig from '../_partials/_basic-cedar-config.mdx'; +import AuthTroubleshooting from '../_partials/_auth-troubleshooting.mdx'; + +This guide shows you how to secure your MCP servers in Kubernetes using +authentication and authorization with the ToolHive Operator. + +:::info + +Authentication and authorization are emerging capabilities in the MCP ecosystem. +The official MCP authorization specification is still evolving, and client +support for these features is limited. ToolHive is leading the way in +implementing these capabilities, but you may encounter some limitations with +certain clients. + +::: + +## Prerequisites + +You'll need: + +- Kubernetes cluster with RBAC enabled +- ToolHive Operator installed (see + [Deploy the ToolHive Operator](./deploy-operator.mdx)) +- `kubectl` access to your cluster + +## Choose your authentication approach + +There are several ways to authenticate with MCP servers running in Kubernetes: + +### Approach 1: External identity provider authentication + +Use this when you want to authenticate users or external services using +providers like Google, GitHub, Microsoft Entra ID, Okta, or Auth0. + +**Prerequisites for external IdP:** + + + +### Approach 2: Shared OIDC configuration with MCPOIDCConfig + +Use this when you want to share the same OIDC configuration across multiple +MCPServers or VirtualMCPServers. The `MCPOIDCConfig` CRD provides a dedicated, +validated resource for managing shared OIDC settings at the platform level. + +**Prerequisites for shared OIDC:** + +- External identity provider configured (same as Approach 1) + +### Approach 3: Shared OIDC configuration with ConfigMap (legacy) + +:::warning[Prefer MCPOIDCConfig] + +For new deployments, use Approach 2 (`MCPOIDCConfig`) instead of a ConfigMap. +`MCPOIDCConfig` provides built-in validation, status tracking, and lifecycle +management. + +::: + +This approach predates MCPOIDCConfig and remains supported. Use this when you +want to share OIDC configuration via a ConfigMap. + +### Approach 4: Kubernetes service-to-service authentication + +Use this when you have client applications running in the same Kubernetes +cluster that need to call MCP servers. This approach uses Kubernetes service +account tokens for authentication. + +**Prerequisites for service-to-service:** + +- Client applications running in Kubernetes pods +- Understanding of Kubernetes service accounts and RBAC + +### Approach 5: Embedded authorization server authentication + +Use this when you want ToolHive to handle the full OAuth flow, including +redirecting users to an upstream identity provider for authentication. This +approach is ideal for MCP servers that accept `Authorization: Bearer` tokens. + +For conceptual background, see +[Embedded authorization server](../concepts/auth-framework.mdx#embedded-authorization-server). + +**Prerequisites for embedded authorization server:** + +- An upstream identity provider that supports the OAuth 2.0 authorization code + flow (such as Okta, Microsoft Entra ID, Auth0, or any OIDC-compliant provider) +- A registered OAuth application/client with your upstream provider +- Client ID and client secret from your upstream provider + +## Set up shared OIDC configuration with MCPOIDCConfig + +The `MCPOIDCConfig` CRD lets you define OIDC provider settings once and +reference them from multiple MCPServer or VirtualMCPServer resources. Each +server specifies its own `audience` (and optionally `scopes`) to maintain token +isolation. + +**Step 1: Create an MCPOIDCConfig resource** + + + + +```yaml title="shared-oidc-config.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPOIDCConfig +metadata: + name: production-oidc + namespace: toolhive-system +spec: + type: inline + inline: + issuer: 'https://auth.example.com' + clientId: 'your-client-id' + clientSecretRef: + name: oidc-secret + key: client-secret + jwksUrl: 'https://auth.example.com/.well-known/jwks.json' +``` + + + + +```yaml title="k8s-oidc-config.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPOIDCConfig +metadata: + name: k8s-sa-oidc + namespace: toolhive-system +spec: + type: kubernetesServiceAccount + kubernetesServiceAccount: + serviceAccount: mcp-client + namespace: client-apps +``` + + + + +Apply the resource: + +```bash +kubectl apply -f .yaml +``` + +**Step 2: Reference MCPOIDCConfig from an MCPServer** + +Use `oidcConfigRef` instead of inline `oidcConfig`. Each server must set a +unique `audience` to prevent token replay across servers: + +```yaml title="mcp-server-shared-oidc.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: weather-server + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/weather-mcp/server + transport: streamable-http + proxyPort: 8080 + permissionProfile: + type: builtin + name: network + # highlight-start + oidcConfigRef: + name: production-oidc + audience: weather-server + scopes: + - openid + # highlight-end +``` + +```bash +kubectl apply -f mcp-server-shared-oidc.yaml +``` + +**Step 3: Verify** + +Check the MCPOIDCConfig status: + +```bash +kubectl get mcpoidc -n toolhive-system +``` + +The `REFERENCES` column shows which workloads use this config. The `READY` +column confirms validation passed. + +### Benefits of MCPOIDCConfig + +- **Centralized management**: update provider settings in one place for all + servers +- **Built-in validation**: CEL rules catch misconfiguration at admission time +- **Status tracking**: see which workloads reference the config and whether it + is valid +- **Lifecycle management**: deletion is blocked while workloads reference the + config + +:::info[Inline oidcConfig deprecation] + +The inline `spec.oidcConfig` field on MCPServer is deprecated and will be +removed in `v1beta1`. Use `oidcConfigRef` to reference a shared MCPOIDCConfig +resource instead. You cannot set both fields on the same MCPServer. + +::: + +## Set up external identity provider authentication + +:::note[Consider MCPOIDCConfig] + +For new deployments, consider using `oidcConfigRef` with a shared MCPOIDCConfig +resource instead of inline `oidcConfig`. See +[Set up shared OIDC configuration](#set-up-shared-oidc-configuration-with-mcpoidcconfig) +above. + +::: + +**Step 1: Create an MCPServer with external OIDC** + +Create an `MCPServer` resource configured to accept tokens from your external +identity provider. The ToolHive proxy will handle authentication before +forwarding requests to the MCP server. + +```yaml title="mcp-server-external-auth.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: weather-server-external + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/weather-mcp/server + transport: sse + proxyPort: 8080 + permissionProfile: + type: builtin + name: network + # Authentication configuration for external IdP + oidcConfig: + type: inline + inline: + issuer: 'https://your-oidc-issuer.com' + audience: 'your-audience' + clientId: 'your-client-id' + jwksUrl: 'https://your-oidc-issuer.com/path/to/jwks' + resources: + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' +``` + +Replace the OIDC placeholders with your actual identity provider configuration. + +**Step 2: Apply the MCPServer resource** + +```bash +kubectl apply -f mcp-server-external-auth.yaml +``` + +**Step 3: Test external authentication** + +Clients connecting to this MCP server must include a valid JWT token from your +configured identity provider in their requests. The ToolHive proxy will validate +the token before allowing access to the MCP server. + +:::note[Obtaining JWT tokens] + +How to obtain JWT tokens varies by identity provider and is outside the scope of +this guide. Consult your identity provider's documentation for specific +instructions on: + +- Interactive user authentication flows (OAuth 2.0 Authorization Code flow) +- Service-to-service authentication (Client Credentials flow) +- API token generation and management + +For Kubernetes service accounts, tokens are automatically mounted at +`/var/run/secrets/kubernetes.io/serviceaccount/token` in pods. + +::: + +## Set up shared OIDC configuration with ConfigMap + +**Step 1: Create OIDC ConfigMap** + +Create a ConfigMap containing the OIDC configuration: + +```yaml title="shared-oidc-config.yaml" +apiVersion: v1 +kind: ConfigMap +metadata: + name: shared-oidc-config + namespace: toolhive-system +data: + issuer: 'https://auth.example.com' + audience: 'https://mcp.example.com' + clientId: 'shared-client-id' + jwksUrl: 'https://auth.example.com/.well-known/jwks.json' +``` + +```bash +kubectl apply -f shared-oidc-config.yaml +``` + +**Step 2: Reference ConfigMap in MCPServer** + +Create MCPServer resources that reference the shared configuration: + +```yaml title="mcp-server-with-configmap-oidc.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: weather-server-shared-oidc + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/weather-mcp/server + transport: sse + proxyPort: 8080 + permissionProfile: + type: builtin + name: network + # Reference shared OIDC configuration + oidcConfig: + type: configMap + configMap: + name: shared-oidc-config + resources: + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' +``` + +```bash +kubectl apply -f mcp-server-with-configmap-oidc.yaml +``` + +### Benefits of ConfigMap approach + +- **Centralized management**: Update OIDC settings in one place +- **Consistency**: Ensure all MCPServers use identical authentication config +- **GitOps friendly**: Manage configuration separately from MCPServer resources +- **Multi-server deployments**: Deploy multiple servers with same auth easily + +## Set up Kubernetes service-to-service authentication + +This approach is ideal when you have client applications running in the same +Kubernetes cluster that need to call MCP servers. + +**Step 1: Create service account for client application** + +Create a service account that your client application will use: + +```yaml title="client-service-account.yaml" +apiVersion: v1 +kind: ServiceAccount +metadata: + name: mcp-client + namespace: client-apps +``` + +```bash +kubectl apply -f client-service-account.yaml +``` + +**Step 2: Create MCPServer for service-to-service auth** + +Create an `MCPServer` resource configured to accept Kubernetes service account +tokens: + +```yaml title="mcp-server-k8s-auth.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: weather-server-k8s + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/weather-mcp/server + transport: sse + proxyPort: 8080 + permissionProfile: + type: builtin + name: network + # Authentication configuration for Kubernetes service accounts + oidcConfig: + type: kubernetes + kubernetes: + serviceAccount: 'mcp-client' + namespace: 'client-apps' + audience: 'toolhive' + issuer: 'https://kubernetes.default.svc' + jwksUrl: 'https://kubernetes.default.svc/openid/v1/jwks' + resources: + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' +``` + +This configuration only allows requests from pods using the `mcp-client` service +account in the `client-apps` namespace. + +```bash +kubectl apply -f mcp-server-k8s-auth.yaml +``` + +**Step 3: Deploy client application with service account** + +Deploy your client application using the service account: + +```yaml title="client-app.yaml" +apiVersion: apps/v1 +kind: Deployment +metadata: + name: mcp-client-app + namespace: client-apps +spec: + replicas: 1 + selector: + matchLabels: + app: mcp-client-app + template: + metadata: + labels: + app: mcp-client-app + spec: + serviceAccountName: mcp-client + containers: + - name: client + image: your-client-app:latest + env: + - name: MCP_SERVER_URL + value: 'http://weather-server-k8s.toolhive-system.svc.cluster.local:8080' +``` + +```bash +kubectl apply -f client-app.yaml +``` + +Your client application can now authenticate to the MCP server using its +Kubernetes service account token, which is automatically mounted at +`/var/run/secrets/kubernetes.io/serviceaccount/token`. + +## Set up embedded authorization server authentication + +The embedded authorization server runs an OAuth authorization server within the +ToolHive proxy. It handles the full OAuth flow by redirecting users to your +upstream identity provider for authentication, then issuing JWTs that the proxy +validates on subsequent requests. This provides MCP servers with +`Authorization: Bearer` tokens without requiring separate authorization server +infrastructure. + +This setup uses the `MCPExternalAuthConfig` custom resource, following the same +pattern as [token exchange configuration](./token-exchange-k8s.mdx). + +**Step 1: Create a Secret for the upstream provider client credentials** + +Store the OAuth client secret for your upstream identity provider: + +```yaml title="upstream-idp-secret.yaml" +apiVersion: v1 +kind: Secret +metadata: + name: upstream-idp-secret + namespace: toolhive-system +type: Opaque +stringData: + client-secret: '' +``` + +```bash +kubectl apply -f upstream-idp-secret.yaml +``` + +**Step 2: Create a Secret for JWT signing keys** + +The embedded authorization server signs JWTs with a private key you provide. +Generate a PEM-encoded private key (RSA or EC), for example: + +```bash +openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 -out signing-key.pem +``` + +Then create a Secret containing the key: + +```yaml title="auth-server-signing-key.yaml" +apiVersion: v1 +kind: Secret +metadata: + name: auth-server-signing-key + namespace: toolhive-system +type: Opaque +stringData: + signing-key: | + -----BEGIN PRIVATE KEY----- + + -----END PRIVATE KEY----- +``` + +```bash +kubectl apply -f auth-server-signing-key.yaml +``` + +:::tip[Key rotation] + +For key rotation, you can reference multiple signing key Secrets in the +`signingKeySecretRefs` list. The first key is used for signing new tokens. +Additional keys are used for verification only, so tokens signed before rotation +remain valid. + +::: + +**Step 3: Create a Secret for HMAC keys** + +The embedded authorization server uses a symmetric HMAC key to sign +authorization codes and refresh tokens. The key must be at least 32 bytes and +cryptographically random, for example: + +```bash +openssl rand -base64 32 +``` + +```yaml title="auth-server-hmac-secret.yaml" +apiVersion: v1 +kind: Secret +metadata: + name: auth-server-hmac-secret + namespace: toolhive-system +type: Opaque +stringData: + hmac-key: '' +``` + +```bash +kubectl apply -f auth-server-hmac-secret.yaml +``` + +:::warning[Ephemeral keys for development only] + +If you omit the `signingKeySecretRefs` and `hmacSecretRefs` fields, ToolHive +generates ephemeral keys that are lost on pod restart. All previously issued +tokens become invalid after a restart. Only omit these Secrets for development +and testing. + +::: + +**Step 4: Create the MCPExternalAuthConfig resource** + +Create an `MCPExternalAuthConfig` resource with the `embeddedAuthServer` type. +This example configures an OIDC upstream provider (the most common case): + +```yaml title="embedded-auth-config.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPExternalAuthConfig +metadata: + name: embedded-auth-server + namespace: toolhive-system +spec: + type: embeddedAuthServer + embeddedAuthServer: + issuer: 'https://mcp.example.com' + signingKeySecretRefs: + - name: auth-server-signing-key + key: signing-key + hmacSecretRefs: + - name: auth-server-hmac-secret + key: hmac-key + tokenLifespans: + accessTokenLifespan: '1h' + refreshTokenLifespan: '168h' + authCodeLifespan: '10m' + upstreamProviders: + - name: google + type: oidc + oidcConfig: + issuerUrl: 'https://accounts.google.com' + clientId: '' + clientSecretRef: + name: upstream-idp-secret + key: client-secret + scopes: + - openid + - profile + - email +``` + +```bash +kubectl apply -f embedded-auth-config.yaml +``` + +**Configuration reference:** + +| Field | Description | +| ---------------------- | ---------------------------------------------------------------------------------------------------------------------- | +| `issuer` | HTTPS URL identifying this authorization server. Appears in the `iss` claim of issued JWTs. | +| `signingKeySecretRefs` | References to Secrets containing JWT signing keys. First key is active; additional keys support rotation. | +| `hmacSecretRefs` | References to Secrets with symmetric keys for signing authorization codes and refresh tokens. | +| `tokenLifespans` | Configurable durations for access tokens (default: 1h), refresh tokens (default: 168h), and auth codes (default: 10m). | +| `upstreamProviders` | Configuration for the upstream identity provider. Currently supports one provider. | + +**Step 5: Create the MCPServer resource** + +The MCPServer needs two configuration references: `externalAuthConfigRef` +enables the embedded authorization server, and `oidcConfig` validates the JWTs +that the embedded authorization server issues. Unlike approaches 1-3 where +`oidcConfig` points to an external identity provider, here it points to the +embedded authorization server itself—the `oidcConfig` issuer must match the +`issuer` in your `MCPExternalAuthConfig`. + +```yaml title="mcp-server-embedded-auth.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: weather-server-embedded + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/weather-mcp/server + transport: streamable-http + proxyPort: 8080 + permissionProfile: + type: builtin + name: network + # Reference the embedded authorization server configuration + externalAuthConfigRef: + name: embedded-auth-server + # Validate JWTs issued by the embedded authorization server + oidcConfig: + type: inline + resourceUrl: 'https://mcp.example.com/mcp' + inline: + # This must match the embedded authorization server issuer url + issuer: 'https://mcp.example.com' + resources: + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' +``` + +```bash +kubectl apply -f mcp-server-embedded-auth.yaml +``` + +:::note + +The `oidcConfig` issuer must match the `issuer` in your `MCPExternalAuthConfig`. +The embedded authorization server exposes a JWKS endpoint that the proxy uses to +validate the JWTs it issues. The proxy also exposes OAuth discovery endpoints +(`/.well-known/oauth-authorization-server`) so MCP clients can discover the +authorization endpoints automatically. + +::: + +### Configure session storage + +By default, the embedded authorization server stores sessions in memory. +Upstream tokens are lost when pods restart, requiring users to re-authenticate. +For production deployments, configure Redis Sentinel as the storage backend by +adding a `storage` block to your `MCPExternalAuthConfig`: + +```yaml title="storage block for MCPExternalAuthConfig" +storage: + type: redis + redis: + sentinelConfig: + masterName: mymaster + sentinelService: + name: redis-sentinel + namespace: redis + aclUserConfig: + usernameSecretRef: + name: redis-acl-secret + key: username + passwordSecretRef: + name: redis-acl-secret + key: password +``` + +Create the Secret containing your Redis ACL credentials: + +```bash +kubectl create secret generic redis-acl-secret \ + --namespace toolhive-system \ + --from-literal=username=toolhive-auth \ + --from-literal=password="YOUR_REDIS_ACL_PASSWORD" +``` + +For a complete walkthrough including deploying Redis Sentinel from scratch, see +[Redis Sentinel session storage](./redis-session-storage.mdx). + +### Using an OAuth 2.0 upstream provider + +If your upstream identity provider does not support OIDC discovery, you can +configure it as an OAuth 2.0 provider with explicit endpoints. This is useful +for providers like GitHub that use OAuth 2.0 but don't implement the full OIDC +specification. + +```yaml title="embedded-auth-oauth2-config.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPExternalAuthConfig +metadata: + name: embedded-auth-oauth2 + namespace: toolhive-system +spec: + type: embeddedAuthServer + embeddedAuthServer: + issuer: 'https://mcp.example.com' + signingKeySecretRefs: + - name: auth-server-signing-key + key: signing-key + hmacSecretRefs: + - name: auth-server-hmac-secret + key: hmac-key + upstreamProviders: + - name: github + type: oauth2 + oauth2Config: + authorizationEndpoint: 'https://github.com/login/oauth/authorize' + tokenEndpoint: 'https://github.com/login/oauth/access_token' + userInfo: + endpointUrl: 'https://api.github.com/user' + httpMethod: GET + additionalHeaders: + Accept: 'application/vnd.github+json' + fieldMapping: + subjectFields: + - id + - login + nameFields: + - name + - login + emailFields: + - email + clientId: '' + clientSecretRef: + name: upstream-idp-secret + key: client-secret + scopes: + - user:email + - read:user +``` + +:::note + +OAuth 2.0 providers require explicit endpoint configuration and a `userInfo` +section, unlike OIDC providers which auto-discover these from the issuer URL. +The `fieldMapping` section maps provider-specific response fields to standard +user identity fields. For example, GitHub returns `login` instead of the +standard `name` field. + +::: + +## Set up authorization + +All authentication approaches can use the same authorization configuration using +Cedar policies. When using the embedded authorization server (Approach 5), Cedar +policies can also evaluate upstream identity provider claims such as group +membership. See +[Upstream identity provider claims](../concepts/cedar-policies.mdx#upstream-identity-provider-claims) +for details. + +**Step 1: Create authorization configuration** + + + +**Step 2: Create a ConfigMap with policies** + +Store your authorization configuration in a ConfigMap: + +```yaml title="authz-configmap.yaml" +apiVersion: v1 +kind: ConfigMap +metadata: + name: authz-config + namespace: toolhive-system +data: + authz-config.json: | + { + "version": "1.0", + "type": "cedarv1", + "cedar": { + "policies": [ + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"weather\");", + "permit(principal == Client::\"alice123\", action == Action::\"call_tool\", resource == Tool::\"admin_tool\");", + "permit(principal, action == Action::\"call_tool\", resource) when { principal.claim_roles.contains(\"premium\") };" + ], + "entities_json": "[]" + } + } +``` + +```bash +kubectl apply -f authz-configmap.yaml +``` + +**Step 3: Update MCPServer to use authorization** + +Add the authorization configuration to your `MCPServer` resources: + +```yaml title="mcp-server-with-authz.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: weather-server-with-authz + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/weather-mcp/server + transport: sse + proxyPort: 8080 + permissionProfile: + type: builtin + name: network + # Authentication configuration + oidcConfig: + type: kubernetes + kubernetes: + serviceAccount: 'mcp-client' + namespace: 'client-apps' + audience: 'toolhive' + issuer: 'https://kubernetes.default.svc' + jwksUrl: 'https://kubernetes.default.svc/openid/v1/jwks' + # Authorization configuration + authzConfig: + type: configMap + configMap: + name: authz-config + key: authz-config.json + resources: + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' +``` + +```bash +kubectl apply -f mcp-server-with-authz.yaml +``` + +## Test your setup + +### Test external IdP authentication + +1. Deploy the external IdP configuration +2. Obtain a valid JWT token from your identity provider +3. Make a request to the MCP server including the token + +### Test service-to-service authentication + +1. Deploy both the MCP server and client application +2. Check that the client can successfully call the MCP server +3. Verify authentication in the ToolHive proxy logs: + +```bash +kubectl logs -n toolhive-system -l app.kubernetes.io/name=weather-server-k8s +``` + +### Test embedded authorization server authentication + +1. Deploy the `MCPExternalAuthConfig` and `MCPServer` resources +2. Check that the MCPServer is running: + + ```bash + kubectl get mcpserver -n toolhive-system weather-server-embedded + ``` + +3. If the server is exposed outside the cluster, verify the OAuth discovery + endpoint is available: + + ```bash + curl https:///.well-known/oauth-authorization-server + ``` + +4. Connect with an MCP client that supports the MCP OAuth specification. The + client should be redirected to your upstream identity provider for + authentication. +5. Check the proxy logs for successful authentication: + + ```bash + kubectl logs -n toolhive-system \ + -l app.kubernetes.io/name=weather-server-embedded + ``` + +### Test authorization + +1. Make requests that should be permitted by your policies +2. Make requests that should be denied +3. Check the proxy logs to see authorization decisions + +## Next steps + +- [Configure token exchange](./token-exchange-k8s.mdx) to let MCP servers + authenticate to backend services +- [Set up audit logging](./logging.mdx) to track authentication decisions and + MCP server activity + +## Related information + +- For conceptual understanding, see + [Authentication and authorization framework](../concepts/auth-framework.mdx) +- For conceptual background on the embedded authorization server, see + [Embedded authorization server](../concepts/auth-framework.mdx#embedded-authorization-server) +- For a similar configuration pattern using token exchange, see + [Configure token exchange](./token-exchange-k8s.mdx) +- For detailed Cedar policy syntax, see + [Cedar policies](../concepts/cedar-policies.mdx) and the + [Cedar documentation](https://docs.cedarpolicy.com/) +- For a complete end-to-end example with Okta OIDC and role-based access + control, see [Role-based authorization with Okta](../integrations/okta.mdx) +- For running MCP servers without authentication, see + [Run MCP servers in Kubernetes](./run-mcp-k8s.mdx) +- For ToolHive Operator installation, see + [Deploy the ToolHive Operator](./deploy-operator.mdx) + +## Troubleshooting + + + +
+Kubernetes-specific issues + +**MCPServer resource issues:** + +- Check the MCPServer status: `kubectl get mcpserver -n toolhive-system` +- Describe the resource for details: + `kubectl describe mcpserver weather-server-k8s -n toolhive-system` + +**Service account issues:** + +- Verify the service account exists: `kubectl get sa -n client-apps mcp-client` +- Check RBAC permissions if needed + +**ConfigMap mounting issues:** + +- Verify the ConfigMap exists: + `kubectl get configmap -n toolhive-system authz-config` +- Check the ConfigMap content: + `kubectl get configmap authz-config -n toolhive-system -o yaml` + +**OIDC configuration issues:** + +- For external IdP: Ensure the issuer URL is accessible from within the cluster +- For Kubernetes auth: Ensure the Kubernetes API server has OIDC enabled +- Check that the JWKS URL returns valid keys + +**Network connectivity:** + +- Verify pods can reach the Kubernetes API server +- Check cluster DNS resolution +- Test service-to-service connectivity: + `kubectl exec -n client-apps deployment/mcp-client-app -- curl http://weather-server-k8s.toolhive-system.svc.cluster.local:8080` + +**ToolHive Operator issues:** + +- Check operator logs: + `kubectl logs -n toolhive-system -l app.kubernetes.io/name=toolhive-operator` +- Verify the operator is running: `kubectl get pods -n toolhive-system` + +
+ +
+Embedded authorization server issues + +**OAuth flow not initiating:** + +- Verify the `MCPExternalAuthConfig` resource exists in the same namespace: + `kubectl get mcpexternalauthconfig -n toolhive-system` +- Check that the `externalAuthConfigRef.name` in your `MCPServer` matches the + `MCPExternalAuthConfig` resource name +- Verify the upstream provider's client ID and redirect URI are correctly + configured in the `MCPExternalAuthConfig` + +**Token validation failures after restart:** + +- Ensure you have configured `signingKeySecretRefs` and `hmacSecretRefs` with + persistent keys +- Without these, ephemeral keys are generated on startup, invalidating all + previously issued tokens + +**Upstream IdP redirect errors:** + +- Verify the redirect URI configured in your upstream provider matches the + ToolHive proxy's callback URL (typically + `https:///oauth/callback`) +- Check that the upstream provider's issuer URL is accessible from within the + cluster +- For OIDC providers, ensure the `/.well-known/openid-configuration` endpoint is + reachable from the proxy pod + +**JWT signing key issues:** + +- Verify signing key Secrets exist: + `kubectl get secret -n toolhive-system auth-server-signing-key` +- Ensure the key format is correct (PEM-encoded RSA or EC private key) +- Check proxy logs for key loading errors: + `kubectl logs -n toolhive-system -l app.kubernetes.io/name=weather-server-embedded` + +**OIDC configuration mismatch:** + +- Ensure the `oidcConfig.inline.issuer` on your `MCPServer` matches the `issuer` + in your `MCPExternalAuthConfig` +- Verify the `resourceUrl` in `oidcConfig` matches the external URL of the MCP + server + +
diff --git a/versioned_docs/version-1.1/toolhive/guides-k8s/connect-clients.mdx b/versioned_docs/version-1.1/toolhive/guides-k8s/connect-clients.mdx new file mode 100644 index 00000000..9ba6ed51 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-k8s/connect-clients.mdx @@ -0,0 +1,1102 @@ +--- +title: Connect clients to MCP servers +description: + Learn how to connect clients to your Kubernetes-hosted MCP servers in + ToolHive. +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Overview + +After deploying MCP servers in your Kubernetes cluster, you need to connect +clients to use them. This guide covers two main connection scenarios: + +1. **External clients** - Connecting from outside the cluster using Ingress or + Gateway API to expose MCP servers +2. **Internal clients** - Connecting from applications running within the same + Kubernetes cluster + +```mermaid +flowchart TB + subgraph External["External clients"] + UI["ToolHive UI"] + CLI["ToolHive CLI"] + Other["Other MCP clients"] + end + + subgraph K8s["Kubernetes cluster"] + Ingress["Ingress/Gateway"] + + subgraph NS["MCP Namespace"] + ProxyService["Service: MCP proxy"] + ProxyPod["Pod: ToolHive proxy"] + MCPPod["Pod: MCP server"] + end + + subgraph AppNS["App namespace"] + App["Your application"] + end + end + + UI -->|HTTPS| Ingress + CLI -->|HTTPS| Ingress + Other -->|HTTPS| Ingress + Ingress --> ProxyService + ProxyService --> ProxyPod + ProxyPod --> MCPPod + + App -->|HTTP| ProxyService +``` + +## Prerequisites + +- A Kubernetes cluster with MCP servers deployed (see + [Run MCP servers in Kubernetes](./run-mcp-k8s.mdx)) +- An Ingress controller or Gateway API implementation installed in your cluster + (for external access) +- [`kubectl`](https://kubernetes.io/docs/tasks/tools/) configured to communicate + with your cluster + +## Connect from outside the cluster + +To make your MCP servers accessible to external clients like the ToolHive UI, +ToolHive CLI, or other MCP clients, you need to expose the proxy service using +an Ingress resource or Gateway API. + +:::info[Service naming convention] + +The ToolHive operator automatically creates a Kubernetes Service for each +MCPServer, MCPRemoteProxy, and VirtualMCPServer resource using the following +naming patterns using the resource name: + +- MCPServer: `mcp--proxy` +- MCPRemoteProxy: `mcp--remote-proxy` +- VirtualMCPServer: `vmcp-` + +For example, an MCPServer named `fetch` gets a Service named `mcp-fetch-proxy`. + +::: + +:::warning[Security requirements] + +When exposing MCP servers externally, you should: + +- Always use HTTPS with valid TLS certificates +- Configure authentication to control access (see + [Authentication and authorization](./auth-k8s.mdx)) +- Consider network policies to restrict traffic + +Running MCP servers without authentication on public networks is a security +risk. + +::: + +### Option 1: Using Ingress + +Ingress provides a stable API for exposing HTTP/HTTPS services. This example +shows a generic Ingress configuration that works with popular Ingress +controllers like Traefik, Contour, and HAProxy, as well as cloud provider +implementations like AWS Load Balancer Controller, Google Cloud Load Balancer, +and Azure Application Gateway. + +First, ensure you have an MCP server deployed. This example uses the `fetch` +server: + +```yaml title="fetch-server.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: fetch + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/gofetch/server:latest + transport: streamable-http + mcpPort: 8080 + proxyPort: 8080 +``` + +Create an Ingress resource to expose the MCP server proxy. You can use either +host-based routing (separate subdomain per server) or path-based routing (single +domain with paths): + + + + +Each MCP server gets its own subdomain: + +```yaml title="fetch-ingress.yaml" +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: fetch-mcp-ingress + namespace: toolhive-system + annotations: + cert-manager.io/cluster-issuer: 'letsencrypt-prod' +spec: + ingressClassName: traefik + tls: + - hosts: + - fetch-mcp.example.com + secretName: fetch-mcp-tls + rules: + - host: fetch-mcp.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: mcp-fetch-proxy + port: + number: 8080 +``` + +The MCP server is accessible at `https://fetch-mcp.example.com/mcp`. + + + + +Multiple MCP servers share a single domain using path prefixes. This approach +requires URL rewriting to strip the path prefix before forwarding to the backend +service. + +:::note + +Path rewriting support and syntax varies by Ingress controller. Check your +controller's documentation for the correct annotations or resources. + +::: + +```yaml title="mcp-ingress.yaml" +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: mcp-servers-ingress + namespace: toolhive-system + annotations: + cert-manager.io/cluster-issuer: 'letsencrypt-prod' + # Traefik example: strip path prefix + traefik.ingress.kubernetes.io/router.middlewares: toolhive-system-strip-mcp-prefix@kubernetescrd +spec: + ingressClassName: traefik + tls: + - hosts: + - mcp.example.com + secretName: mcp-tls + rules: + - host: mcp.example.com + http: + paths: + # Fetch MCP server + - path: /fetch + pathType: Prefix + backend: + service: + name: mcp-fetch-proxy + port: + number: 8080 + # Another MCP server + - path: / + pathType: Prefix + backend: + service: + name: mcp--proxy + port: + number: 8080 +--- +# Traefik Middleware to strip path prefixes +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: strip-mcp-prefix + namespace: toolhive-system +spec: + stripPrefix: + prefixes: + - /fetch + - / +``` + +The MCP servers are accessible at `https://mcp.example.com/fetch/mcp` and +`https://mcp.example.com//mcp`. + + + + +This example is the same as the previous path-based routing example but includes +additional rules in the Ingress to direct the `.well-known` path for each MCP +server to the corresponding backend. This is necessary when using OAuth +authentication since the OAuth flow requires access to the `.well-known` +endpoint for discovery. + +First, in the MCPServer spec for each server, ensure the `resourceUrl` property +is set to the full client-facing URL: + +```yaml title="fetch-server-oauth.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +# ... +spec: + oidcConfig: + type: inline + resourceUrl: https://mcp.example.com//mcp + inline: + # ... other OIDC config ... +``` + +The `inline.audience` value should match the audience expected by your identity +provider, and is likely the same for all servers using the same authorization +server. See [Authentication and authorization](./auth-k8s.mdx) for full OIDC +setup instructions. + +Configure the Ingress with additional rules for the `.well-known` paths of each +MCP server that has OAuth enabled: + +:::note + +Path rewriting support and syntax varies by Ingress controller. Check your +controller's documentation for the correct annotations or resources. + +::: + +```yaml {28-34,43-49} title="mcp-ingress.yaml" +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: mcp-servers-ingress + namespace: toolhive-system + annotations: + cert-manager.io/cluster-issuer: 'letsencrypt-prod' + # Traefik example: strip path prefix + traefik.ingress.kubernetes.io/router.middlewares: toolhive-system-strip-mcp-prefix@kubernetescrd +spec: + ingressClassName: traefik + tls: + - hosts: + - mcp.example.com + secretName: mcp-tls + rules: + - host: mcp.example.com + http: + paths: + # Fetch MCP server + - path: /fetch + pathType: Prefix + backend: + service: + name: mcp-fetch-proxy + port: + number: 8080 + - path: /.well-known/oauth-protected-resource/fetch/mcp + pathType: Exact + backend: + service: + name: mcp-fetch-proxy + port: + number: 8080 + # Another MCP server + - path: / + pathType: Prefix + backend: + service: + name: mcp--proxy + port: + number: 8080 + - path: /.well-known/oauth-protected-resource//mcp + pathType: Exact + backend: + service: + name: mcp--proxy + port: + number: 8080 +--- +# Traefik Middleware to strip path prefixes +apiVersion: traefik.io/v1alpha1 +kind: Middleware +metadata: + name: strip-mcp-prefix + namespace: toolhive-system +spec: + stripPrefix: + prefixes: + - /fetch + - / +``` + +The MCP servers are accessible at `https://mcp.example.com/fetch/mcp` and +`https://mcp.example.com//mcp`. + + + + +Apply the resources: + +```bash +kubectl apply -f fetch-server.yaml +kubectl apply -f fetch-ingress.yaml # or mcp-ingress.yaml for path-based +``` + +Verify the Ingress is configured: + +```bash +kubectl get ingress -n toolhive-system +``` + +### Option 2: Using Gateway API + +The [Gateway API](https://gateway-api.sigs.k8s.io/) is a more expressive way to +expose services and is the successor to Ingress. This example works with Gateway +API implementations like Cilium, Istio, Envoy Gateway, and Traefik, as well as +cloud provider implementations like AWS Gateway API Controller, Google +Kubernetes Engine (GKE) Gateway controller, and Azure Application Gateway for +Containers. See the +[full list of implementations](https://gateway-api.sigs.k8s.io/implementations/). + +:::tip + +For a complete working example using the ngrok Gateway API implementation, see +the +[Configure secure ingress for MCP servers on Kubernetes](../integrations/ingress-ngrok.mdx) +tutorial. + +::: + +Many Gateway API implementations create a Gateway resource automatically during +installation. For example, Traefik's Helm chart creates a `traefik-gateway` in +the default namespace when enabled. Check if a Gateway already exists: + +```bash +kubectl get gateway --all-namespaces +``` + +If a Gateway exists, note its name and namespace to use in your HTTPRoute. If +you need to create a new Gateway, use this example: + +```yaml title="mcp-gateway.yaml" +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: mcp-gateway + namespace: toolhive-system +spec: + gatewayClassName: traefik # Change to match your Gateway implementation + listeners: + - name: https + protocol: HTTPS + port: 443 + tls: + mode: Terminate + certificateRefs: + - name: mcp-gateway-cert + allowedRoutes: + namespaces: + from: Same +``` + +Create an HTTPRoute to expose your MCP server. You can use either host-based +routing (separate subdomain per server) or path-based routing (single domain +with paths): + + + + +Each MCP server gets its own subdomain: + +```yaml title="fetch-route.yaml" +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: fetch-mcp-route + namespace: toolhive-system +spec: + parentRefs: + - name: mcp-gateway # Reference your Gateway name (e.g., traefik-gateway) + # namespace: default # Uncomment if Gateway is in a different namespace + hostnames: + - fetch-mcp.example.com # Change to your domain + rules: + - backendRefs: + - name: mcp-fetch-proxy # Format: mcp--proxy + port: 8080 # This matches the proxyPort +``` + +The MCP server is accessible at `https://fetch-mcp.example.com/mcp`. + + + + +Multiple MCP servers share a single domain using path prefixes. This approach +uses URL rewriting to strip the path prefix before forwarding to the backend +service. + +```yaml title="mcp-routes.yaml" +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: mcp-servers-route + namespace: toolhive-system +spec: + parentRefs: + - name: mcp-gateway # Reference your Gateway name (e.g., traefik-gateway) + # namespace: default # Uncomment if Gateway is in a different namespace + hostnames: + - mcp.example.com # Change to your domain + rules: + # Fetch MCP server + - matches: + - path: + type: PathPrefix + value: /fetch + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplacePrefixMatch + replacePrefixMatch: / + backendRefs: + - name: mcp-fetch-proxy # Format: mcp--proxy + port: 8080 # This matches the proxyPort + # Another MCP server + - matches: + - path: + type: PathPrefix + value: / + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplacePrefixMatch + replacePrefixMatch: / + backendRefs: + - name: mcp--proxy + port: 8080 +``` + +The MCP servers are accessible at `https://mcp.example.com/fetch/mcp` and +`https://mcp.example.com//mcp`. + +The `URLRewrite` filter removes the path prefix (e.g., `/fetch`) before +forwarding requests to the backend service, so the MCP server receives requests +at `/mcp` as expected. + + + + +This example is the same as the previous path-based routing example but includes +additional rules in the HTTPRoute to direct the `.well-known` path for each MCP +server to the corresponding backend. This is necessary when using OAuth +authentication since the OAuth flow requires access to the `.well-known` +endpoint for discovery. + +First, in the MCPServer spec for each server, ensure the `resourceUrl` property +is set to the full client-facing URL: + +```yaml title="fetch-server-oauth.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +# ... +spec: + oidcConfig: + type: inline + resourceUrl: https://mcp.example.com//mcp + inline: + # ... other OIDC config ... +``` + +The `inline.audience` value should match the audience expected by your identity +provider, and is likely the same for all servers using the same authorization +server. See [Authentication and authorization](./auth-k8s.mdx) for full OIDC +setup instructions. + +Configure the HTTPRoute with additional rules for the `.well-known` paths of +each MCP server that has OAuth enabled: + +```yaml {27-33,48-54} title="mcp-routes.yaml" +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: mcp-servers-route + namespace: toolhive-system +spec: + parentRefs: + - name: mcp-gateway # Reference your Gateway name (e.g., traefik-gateway) + # namespace: default # Uncomment if Gateway is in a different namespace + hostnames: + - mcp.example.com # Change to your domain + rules: + # Fetch MCP server + - matches: + - path: + type: PathPrefix + value: /fetch + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplacePrefixMatch + replacePrefixMatch: / + backendRefs: + - name: mcp-fetch-proxy # Format: mcp--proxy + port: 8080 # This matches the proxyPort + - matches: + - path: + type: Exact + value: /.well-known/oauth-protected-resource/fetch/mcp + backendRefs: + - name: mcp-fetch-proxy + port: 8080 + # Another MCP server + - matches: + - path: + type: PathPrefix + value: / + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplacePrefixMatch + replacePrefixMatch: / + backendRefs: + - name: mcp--proxy + port: 8080 + - matches: + - path: + type: Exact + value: /.well-known/oauth-protected-resource//mcp + backendRefs: + - name: mcp--proxy + port: 8080 +``` + +The MCP servers are accessible at `https://mcp.example.com/fetch/mcp` and +`https://mcp.example.com//mcp`. + +The `URLRewrite` filter removes the path prefix (e.g., `/fetch`) before +forwarding requests to the backend service, so the MCP server receives requests +at `/mcp` as expected. + + + + +Apply the resources: + +```bash +kubectl apply -f mcp-gateway.yaml # If creating a new Gateway +kubectl apply -f fetch-route.yaml # or mcp-routes.yaml for path-based +``` + +Verify the route is configured: + +```bash +kubectl get httproute -n toolhive-system +``` + +### TLS certificates + +For production deployments, use valid TLS certificates from a trusted +certificate authority. The most common approaches are: + + + + +[cert-manager](https://cert-manager.io/) automates certificate management in +Kubernetes. Install cert-manager and create a ClusterIssuer: + +```yaml title="letsencrypt-issuer.yaml" +apiVersion: cert-manager.io/v1 +kind: ClusterIssuer +metadata: + name: letsencrypt-prod +spec: + acme: + server: https://acme-v02.api.letsencrypt.org/directory + email: your-email@example.com # Change to your email + privateKeySecretRef: + name: letsencrypt-prod + solvers: + - http01: + ingress: + class: traefik # Change to match your Ingress controller +``` + +Apply it: + +```bash +kubectl apply -f letsencrypt-issuer.yaml +``` + +The Ingress example above already includes the cert-manager annotation. Once +cert-manager is installed, it will automatically provision and renew +certificates. + + + + +If you have existing certificates, create a Kubernetes Secret: + +```bash +kubectl create secret tls fetch-mcp-tls \ + --cert=path/to/tls.crt \ + --key=path/to/tls.key \ + -n toolhive-system +``` + +Reference this secret in your Ingress or Gateway configuration as shown in the +examples above. + + + + +### Connect with ToolHive UI or CLI + +Once your MCP server is exposed with HTTPS, you can connect to it as a remote +MCP server from the ToolHive UI or CLI. + + + + +In the ToolHive UI: + +1. Click **Add an MCP server** on the **MCP Servers** page +2. Select **Add a remote MCP server** +3. Enter the connection details: + - **Name**: A friendly name for the server + - **Server URL**: Use the appropriate URL based on your routing approach: + - Host-based: `https://fetch-mcp.example.com/mcp` + - Path-based: `https://mcp.example.com/fetch/mcp` + - **Transport**: Streamable HTTP (or SSE if your server uses SSE) +4. If authentication is configured, select the method and enter the required + OAuth or OIDC details +5. Click **Install server** + +The MCP server appears in your server list and you can use it with any connected +MCP client. + + + + +Use the `thv run` command to connect: + +```bash +# Host-based routing: separate subdomain per server +thv run --name fetch-k8s https://fetch-mcp.example.com/mcp + +# Path-based routing: single domain with paths +thv run --name fetch-k8s https://mcp.example.com/fetch/mcp +``` + +If authentication is configured, add the appropriate flags. See the +[ToolHive CLI guide](../guides-cli/run-mcp-servers.mdx#authentication-setup) for +details. + +The MCP server is now available to your configured MCP clients. + + + + +For more details on using remote MCP servers, see: + +- [Run remote MCP servers (UI)](../guides-ui/run-mcp-servers.mdx?custom-type=custom_remote#install-a-custom-mcp-server) +- [Run remote MCP servers (CLI)](../guides-cli/run-mcp-servers.mdx#run-a-remote-mcp-server) + +## Connect from within the cluster + +Applications running inside your Kubernetes cluster can connect directly to MCP +server proxy services using Kubernetes service discovery. This is more efficient +and secure than routing through an external Ingress. + +### Service DNS names + +Each MCPServer, MCPRemoteProxy, or VirtualMCPServer automatically gets a +Kubernetes Service that other pods can use to connect using the following naming +patterns: + +- MCPServer: `mcp--proxy` +- MCPRemoteProxy: `mcp--remote-proxy` +- VirtualMCPServer: `vmcp-` + +The full DNS name follows the standard Kubernetes pattern: + +```text +..svc.cluster.local: +``` + +For example, if you have an MCPServer named `fetch` in the `toolhive-system` +namespace with `proxyPort: 8080`, the full URL is: + +```text +http://mcp-fetch-proxy.toolhive-system.svc.cluster.local:8080 +``` + +Within the same namespace, you can use the short form: + +```text +http://mcp-fetch-proxy:8080 +``` + +### Example: Configuring applications + +When deploying applications like AI agents in the same cluster, configure them +to use the service DNS name. This example shows how to pass the connection URL +as an environment variable: + +```yaml title="agent-app.yaml" +apiVersion: apps/v1 +kind: Deployment +metadata: + name: my-agent-app + namespace: my-app +spec: + # ... other deployment configuration ... + template: + spec: + containers: + - name: app + image: my-agent-app:latest + env: + - name: MCP_SERVER_URL + # Different namespace: use full DNS name + value: 'http://mcp-fetch-proxy.toolhive-system.svc.cluster.local:8080/mcp' + # Same namespace: use short name + # value: 'http://mcp-fetch-proxy:8080/mcp' +``` + +### Network policies for cross-namespace access + +If your cluster uses network policies, you may need to create a policy to allow +traffic between namespaces: + +```yaml title="allow-cross-namespace.yaml" +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: allow-app-to-mcp + namespace: toolhive-system +spec: + podSelector: + matchLabels: + app.kubernetes.io/name: toolhive-proxy + policyTypes: + - Ingress + ingress: + - from: + - namespaceSelector: + matchLabels: + name: my-app # Your app's namespace must have this label +``` + +## Authentication for external clients + +When exposing MCP servers externally, configure authentication to control +access. ToolHive supports multiple authentication methods: + +- **OIDC authentication** - Use external identity providers like Google, GitHub, + Okta, or Microsoft Entra ID +- **Kubernetes service accounts** - For service-to-service authentication within + the cluster + +See the [Authentication and authorization](./auth-k8s.mdx) guide for detailed +setup instructions. + +## Check connection status + +### Test external connectivity + +If you have the ToolHive CLI installed, you can test connectivity to your MCP +server: + +```bash +thv mcp list tools --server https://fetch-mcp.example.com/mcp +``` + +Or use `curl` to send a JSON-RPC request: + +```bash +# Host-based routing +curl -X POST https://fetch-mcp.example.com/mcp \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' + +# Path-based routing +curl -X POST https://mcp.example.com/fetch/mcp \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' +``` + +You should receive a JSON response with a list of available tools. + +:::tip + +If you've configured authentication on your MCP server, see the +[Authentication and authorization](./auth-k8s.mdx) guide for how to test +authenticated connections. + +::: + +### Test internal connectivity + +Test connectivity from within the cluster: + +```bash +# Port-forward to test locally +kubectl port-forward -n toolhive-system service/mcp-fetch-proxy 8080:8080 + +# In another terminal, test the connection +curl -X POST http://localhost:8080/mcp \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' +``` + +### Verify the connection path + +Check that the Ingress or Gateway is properly configured and the Service has +running pods: + +```bash +# For Ingress: verify it exists and has an address +kubectl get ingress -n toolhive-system fetch-mcp-ingress + +# For Gateway API: check HTTPRoute status (look for "Accepted: True" in conditions) +kubectl describe httproute -n toolhive-system fetch-mcp-route + +# Verify the Service exists +kubectl get service -n toolhive-system mcp-fetch-proxy + +# Check that the proxy pod is running +kubectl get pods -n toolhive-system -l app.kubernetes.io/instance=fetch +``` + +If the Ingress shows an address or the HTTPRoute status shows "Accepted: True", +and the pod is running, the connection path is properly configured. + +## Next steps + +Learn how to secure your MCP servers with +[Authentication and authorization](./auth-k8s.mdx). + +Configure [Telemetry and metrics](./telemetry-and-metrics.mdx) to monitor your +MCP server usage and performance. + +[Set up logging](./logging.mdx) to track requests and audit MCP server activity. + +## Related information + +- [Run MCP servers in Kubernetes](./run-mcp-k8s.mdx) - Deploy MCP servers in + your cluster +- [Proxy remote MCP servers](./remote-mcp-proxy.mdx) - Create proxies for + external MCP servers +- [Client compatibility](../reference/client-compatibility.mdx) - Supported MCP + clients and configuration +- [Kubernetes CRD reference](../reference/crd-spec.md#apiv1alpha1mcpserver) - + Full MCPServer specification +- [Configure secure ingress tutorial](../integrations/ingress-ngrok.mdx) - + Complete tutorial using ngrok and Gateway API + +## Troubleshooting + +
+Ingress returns 503 Service Unavailable + +If your Ingress returns a 503 error: + +```bash +# Check if the service exists +kubectl get service -n toolhive-system mcp-fetch-proxy + +# Check the proxy pod is running +kubectl get pods -n toolhive-system -l app.kubernetes.io/instance=fetch + +# Check pod logs for errors +kubectl logs -n toolhive-system -l app.kubernetes.io/instance=fetch +``` + +Common causes: + +- **Proxy pod not running**: Ensure the MCPServer resource was created + successfully +- **Wrong service name**: The Ingress backend service name must follow the + naming conventions defined above +- **Wrong port**: The Ingress backend port must match the `proxyPort` in the + MCPServer spec +- **Pod health check failing**: Check the proxy pod logs for errors + +
+ +
+TLS certificate issues + +If you see certificate errors when connecting: + +```bash +# Check the certificate secret exists +kubectl get secret -n toolhive-system fetch-mcp-tls + +# Describe the secret to verify it contains tls.crt and tls.key +kubectl describe secret -n toolhive-system fetch-mcp-tls + +# If using cert-manager, check certificate status +kubectl get certificate -n toolhive-system + +# Check cert-manager logs +kubectl logs -n cert-manager -l app=cert-manager +``` + +Common causes: + +- **Certificate not ready**: Wait for cert-manager to provision the certificate + (can take a few minutes) +- **DNS not configured**: Ensure your domain points to the Ingress load balancer +- **Challenge validation failing**: Check cert-manager logs for ACME challenge + errors +- **Wrong ClusterIssuer**: Verify the cert-manager annotation references an + existing ClusterIssuer + +
+ +
+Authentication failures + +If OAuth authentication is failing when connecting to your MCP server: + +```bash +# Test if the OAuth metadata endpoint is accessible +# For host-based routing +curl https://fetch-mcp.example.com/.well-known/oauth-protected-resource + +# For path-based routing +curl https://mcp.example.com/.well-known/oauth-protected-resource/fetch/mcp + +# Check proxy logs for authentication errors +kubectl logs -n toolhive-system -l app.kubernetes.io/instance=fetch + +# Verify the MCPServer OIDC configuration +kubectl get mcpserver -n toolhive-system fetch -o yaml | grep -A15 oidcConfig +``` + +Common causes: + +- **`.well-known` endpoint not accessible**: When using path-based routing with + OAuth, you must configure your Ingress or HTTPRoute to forward the + `.well-known` path to the backend. See the "Path-based routing with OAuth" tab + in the Ingress or Gateway API sections above for configuration examples. +- **Missing or incorrect `resourceUrl`**: Ensure the `resourceUrl` in your + MCPServer's `oidcConfig` matches the client-facing URL (e.g., + `https://mcp.example.com/fetch/mcp` for path-based routing). +- **Token validation failure**: The token's `aud` claim must match the + configured audience in your MCPServer's OIDC configuration. +- **Issuer mismatch**: The token's `iss` claim must match the configured issuer. +- **JWKS endpoint unreachable**: Check that the proxy can reach your identity + provider's JWKS endpoint to validate tokens. + +For detailed OAuth setup and troubleshooting, see the +[Authentication and authorization](./auth-k8s.mdx) guide. + +
+ +
+Cannot connect from within cluster + +If pods cannot connect to the MCP server service: + +```bash +# Verify service exists +kubectl get service -n toolhive-system mcp-fetch-proxy + +# Check that pods are running +kubectl get pods -n toolhive-system -l app.kubernetes.io/instance=fetch + +# Test DNS resolution from a pod +kubectl run test-dns -n toolhive-system --image=busybox --restart=Never -- \ + nslookup mcp-fetch-proxy.toolhive-system.svc.cluster.local + +# Check the DNS test results +kubectl logs -n toolhive-system test-dns + +# Clean up the test pod +kubectl delete pod -n toolhive-system test-dns + +# Test connectivity from a pod +kubectl run test-curl -n toolhive-system --image=curlimages/curl --restart=Never -- \ + curl -X POST http://mcp-fetch-proxy:8080/mcp \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' + +# Check the connectivity test results +kubectl logs -n toolhive-system test-curl + +# Clean up the test pod +kubectl delete pod -n toolhive-system test-curl +``` + +Common causes: + +- **Network policies blocking traffic**: Check for network policies that might + prevent pod-to-pod communication +- **Wrong namespace**: Ensure you're using the correct service DNS name for + cross-namespace access +- **Service not created**: The operator automatically creates services, but + verify it exists +- **Wrong port**: Ensure you're using the `proxyPort` value from the MCPServer + spec +- **Wrong service name**: Remember the service name follows the naming + conventions defined above + +
+ +
+Gateway API not working + +If using Gateway API and connections fail: + +```bash +# Check Gateway status +kubectl get gateway -n toolhive-system mcp-gateway + +# Check HTTPRoute status +kubectl get httproute -n toolhive-system fetch-mcp-route + +# Describe the HTTPRoute for detailed status +kubectl describe httproute -n toolhive-system fetch-mcp-route + +# Check Gateway implementation logs (example for Istio) +kubectl logs -n istio-system -l app=istio-ingressgateway +``` + +Common causes: + +- **Gateway not ready**: Wait for the Gateway to be accepted and programmed by + the controller +- **Wrong gateway class**: Ensure `gatewayClassName` matches your installed + Gateway API implementation +- **Listener configuration issues**: Verify the Gateway listener configuration + matches the HTTPRoute requirements +- **Certificate issues**: For HTTPS, ensure the certificate reference exists and + is valid + +
+ +
+Cross-namespace access denied + +If cross-namespace connections fail: + +```bash +# Check network policies in the MCP server namespace +kubectl get networkpolicy -n toolhive-system + +# Describe network policies to see rules +kubectl describe networkpolicy -n toolhive-system + +# Check if the app namespace has the required labels +kubectl get namespace my-app --show-labels +``` + +Solutions: + +- Create or update network policies to allow traffic from your app namespace +- Add required labels to your application namespace +- Test connectivity using a debug pod in the app namespace + +
diff --git a/versioned_docs/version-1.1/toolhive/guides-k8s/customize-tools.mdx b/versioned_docs/version-1.1/toolhive/guides-k8s/customize-tools.mdx new file mode 100644 index 00000000..fc3cfc63 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-k8s/customize-tools.mdx @@ -0,0 +1,246 @@ +--- +title: Customize tools +description: Filter and rename MCP server tools using the MCPToolConfig CRD and + toolConfigRef. +--- + +## Overview + +Use the MCPToolConfig Custom Resource Definition (CRD) to centrally manage which +tools an MCP server exposes, and optionally rename tools or override their +descriptions. You reference the configuration from an MCPServer using the +`toolConfigRef` field. + +- toolsFilter: allow-list the tools to expose. +- toolsOverride: rename tools and/or change their descriptions. +- Same-namespace only: an MCPServer can reference only MCPToolConfig objects in + the same namespace. +- The inline `spec.tools` field has been removed. Use `toolConfigRef` instead. + +## Define a basic tool filter + +This example exposes only three tools on a server: + +```yaml title="toolconfig-basic.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPToolConfig +metadata: + name: basic-tool-filter + namespace: toolhive-system +spec: + toolsFilter: + - read_file + - write_file + - list_directory +``` + +:::note[Empty filter] + +If `toolsFilter` is omitted or empty, all tools are allowed. + +::: + +## Rename tools and override descriptions + +You can rename tools to clearly distinguish different deployments or scopes, and +refine their descriptions to add usage guidance. + +A common pattern is running the same MCP server multiple times for different +scopes (for example, separate GitHub orgs, repos, or environments). Renaming +tools makes intent obvious and helps prevent mistakes. + +```yaml title="toolconfig-with-overrides.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPToolConfig +metadata: + name: github-tools-config + namespace: toolhive-system +spec: + # Only expose GitHub PR-related tools + toolsFilter: + - create_pull_request + - get_pull_request + - list_pull_requests + - merge_pull_request + + # You can override name, description, or both (they are independent) + toolsOverride: + # Override only the name + create_pull_request: + name: github_create_pr + + # Override only the description (keep the original name) + get_pull_request: + description: Retrieve details of a specific GitHub pull request + + # Override both name and description + list_pull_requests: + name: github_list_prs + description: List pull requests in a repository + + merge_pull_request: + name: github_merge_pr + description: Merge a GitHub pull request +``` + +The key in the `toolsOverride` map is the original tool name; the `name` field +is the user-visible (overridden) name. + +:::info[Override fields] + +You can override name or description independently. Leave one unset to keep the +original value from the MCP server. Both fields cannot be empty at the same +time. + +::: + +:::tip[When to rename?] + +If you run the same server for different scopes (for example, prod vs. sandbox), +use distinct tool names like `github_prod_create_pr` and +`github_sandbox_create_pr` to make intent clear to clients. + +::: + +## Reference the configuration from an MCP server + +Add `toolConfigRef` to the `spec` section of your MCPServer or MCPRemoteProxy +resource. + + + + +```yaml {10-11} title="mcpserver-with-toolconfig.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: github + namespace: toolhive-system +spec: + image: ghcr.io/github/github-mcp-server + transport: stdio + proxyPort: 8080 + toolConfigRef: + name: github-tools-config + # ... other spec fields ... +``` + + + + +```yaml {10-11} title="mcpremoteproxy-with-toolconfig.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRemoteProxy +metadata: + name: github + namespace: toolhive-system +spec: + remoteUrl: https://github.com/github/github-mcp-server + transport: streamable-http + proxyPort: 8080 + toolConfigRef: + name: github-tools-config + # ... auth config ... +``` + + + + +:::note[Filtering and overrides together] + +When you use `toolsFilter` and `toolsOverride` together, filter by the +user-visible (overridden) names. Tool calls using overridden names are forwarded +to the actual tool. + +::: + +## Example: org-scoped tool names + +Run the GitHub MCP twice, once per organization, and rename tools so intent is +clear to clients. + +```yaml title="github-org-scoped-tools.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPToolConfig +metadata: + name: github-acme-tools + namespace: toolhive-system +spec: + toolsFilter: + - github_acme_create_pr + - github_acme_get_pr + toolsOverride: + create_pull_request: + name: github_acme_create_pr + get_pull_request: + name: github_acme_get_pr +--- +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPToolConfig +metadata: + name: github-foocorp-tools + namespace: toolhive-system +spec: + toolsFilter: + - github_foocorp_create_pr + - github_foocorp_get_pr + toolsOverride: + create_pull_request: + name: github_foocorp_create_pr + get_pull_request: + name: github_foocorp_get_pr +--- +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: github-acme + namespace: toolhive-system +spec: + image: ghcr.io/github/github-mcp-server + transport: stdio + proxyPort: 8080 + # (Use credentials that scope access to the acme-org here) + toolConfigRef: + name: github-acme-tools +--- +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: github-foocorp + namespace: toolhive-system +spec: + image: ghcr.io/github/github-mcp-server + transport: stdio + proxyPort: 8080 + # (Use credentials that scope access to the foo-corp org here) + toolConfigRef: + name: github-foocorp-tools +``` + +## Inspect status and troubleshoot + +Use kubectl to inspect status and track change propagation: + +```bash +kubectl -n toolhive-system get mcptoolconfigs +kubectl -n toolhive-system get mcptoolconfig github-tools-config -o yaml +kubectl -n toolhive-system get mcpserver github -o yaml +``` + +- If an MCPToolConfig is still referenced, deletion is blocked by a finalizer. + Remove all toolConfigRef references first. +- If an MCPServer references a missing MCPToolConfig, the server enters Failed + and the controller logs include the missing name and namespace. + +## Next steps + +- [Secure your servers](./auth-k8s.mdx) with authentication and authorization +- [Monitor server activity](./telemetry-and-metrics.mdx) with OpenTelemetry and + Prometheus + +## Related information + +- See the + [Kubernetes CRD reference](../reference/crd-spec.md#apiv1alpha1mcptoolconfig) + for the full MCPToolConfig and MCPServerSpec schemas. +- Learn how to [run the MKP server in Kubernetes](../guides-mcp/k8s.mdx). diff --git a/versioned_docs/version-1.1/toolhive/guides-k8s/deploy-operator.mdx b/versioned_docs/version-1.1/toolhive/guides-k8s/deploy-operator.mdx new file mode 100644 index 00000000..6c117814 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-k8s/deploy-operator.mdx @@ -0,0 +1,595 @@ +--- +title: Deploy the operator +description: + How to deploy the ToolHive operator in a Kubernetes cluster using Helm or + kubectl +--- + +## Prerequisites + +- A Kubernetes cluster (current and two previous minor versions are supported) +- Permissions to create resources in the cluster +- [`kubectl`](https://kubernetes.io/docs/tasks/tools/) configured to communicate + with your cluster +- [Helm](https://helm.sh/docs/intro/install/) (v3.10 minimum, v3.14+ + recommended) + +## Install the CRDs + +![Latest CRD Helm chart release](https://img.shields.io/github/v/release/stacklok/toolhive?style=for-the-badge&logo=helm&label=Latest%20CRD%20chart&color=bddfc2) + +The ToolHive operator requires Custom Resource Definitions (CRDs) to manage +MCPServer resources. The CRDs define the structure and behavior of MCPServers in +your cluster. + +Choose an installation method based on your needs: + +- **Helm** (recommended): Provides customization options and manages the full + lifecycle of the operator. CRDs are installed and upgraded automatically as + part of the Helm chart. +- **kubectl**: Uses static manifests for a simple installation. Useful for + environments where Helm isn't available or for GitOps workflows. + + + + +This command installs the latest version of the ToolHive operator CRDs Helm +chart: + +```bash +helm upgrade --install toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds \ + -n toolhive-system --create-namespace +``` + +:::warning[Namespace consistency] + +When you install this chart, Helm stamps all CRDs with a +`meta.helm.sh/release-namespace` annotation set to the namespace used at install +time and is fixed for that release. You must continue to use the same namespace +on all future `helm upgrade` commands for the CRDs. If you decide to specify a +different namespace, an error will occur due to ownership issues. + +If you need to migrate to a different namespace, see the +[CRD namespace mismatch troubleshooting](#crd-upgrade-fails-with-namespace-mismatch) +section. + +::: + +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 \ + -n toolhive-system --version 0.12.1 +``` + +#### CRD configuration options + +The Helm chart provides fine-grained control over which CRDs are installed. By +default, all CRDs are installed. You can selectively enable or disable CRD +groups using these values: + +| Value | Description | Default | +| ------------------------- | ----------------------------------------------------- | ------- | +| `crds.install.server` | Install server CRDs (MCPServer, MCPRemoteProxy, etc.) | `true` | +| `crds.install.registry` | Install registry CRDs (MCPRegistry) | `true` | +| `crds.install.virtualMcp` | Install vMCP CRDs (VirtualMCPServer, etc.) | `true` | +| `crds.keep` | Preserve CRDs when uninstalling the chart | `true` | + +For example, to install only server-related CRDs without vMCP support: + +```bash +helm upgrade --install toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds \ + -n toolhive-system --set crds.install.virtualMcp=false +``` + +:::note + +The `crds.keep` option adds the `helm.sh/resource-policy: keep` annotation to +CRDs, which prevents Helm from deleting them during `helm uninstall`. This +protects your custom resources from accidental deletion. If you want to remove +CRDs during uninstall, set `crds.keep=false`. + +::: + + + + +To install the CRDs using `kubectl`, run the following, ensuring you only apply +the CRDs you need for the features you plan to use: + +```bash +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_embeddingservers.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcptoolconfigs.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpremoteproxies.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpservers.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpgroups.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpregistries.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_virtualmcpservers.yaml +``` + +Replace `v0.12.1` in the commands above with your target CRD version. + + + + +## Install the operator + +![Latest Operator Helm chart release](https://img.shields.io/github/v/release/stacklok/toolhive?style=for-the-badge&logo=helm&label=Latest%20Operator%20chart&color=bddfc2) + +To install the ToolHive operator using default settings, run the following +command: + +```bash +helm upgrade --install toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator -n toolhive-system --create-namespace +``` + +This command installs the latest version of the ToolHive operator CRDs Helm +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.12.1 +``` + +Verify the installation: + +```bash +kubectl get pods -n toolhive-system +``` + +After about 30 seconds, you should see the `toolhive-operator` pod running. + +Check the logs of the operator pod: + +```bash +kubectl logs -f -n toolhive-system +``` + +This shows you the logs of the operator pod, which can help you debug any +issues. For comprehensive logging and audit capabilities, see the +[Logging infrastructure](./logging.mdx) guide. + +## Customize the operator + +You can customize the operator installation by providing a `values.yaml` file +with your configuration settings. For example, to change the number of replicas +and set a specific ToolHive version, create a `values.yaml` file: + +```yaml title="values.yaml" +operator: + replicaCount: 2 + toolhiveRunnerImage: ghcr.io/stacklok/toolhive:v0.2.17 # or `latest` +``` + +Install the operator with your custom values: + +```bash {3} +helm upgrade --install toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator\ + -n toolhive-system --create-namespace\ + -f values.yaml +``` + +To see all available configuration options, run: + +```bash +helm show values oci://ghcr.io/stacklok/toolhive/toolhive-operator +``` + +## Operator deployment modes + +The ToolHive operator supports two distinct deployment modes to accommodate +different security requirements and organizational structures. + +### Cluster mode (default) + +Cluster mode provides the operator with cluster-wide access to manage MCPServer +resources in any namespace. This is the default mode and is suitable for +platform teams managing MCPServers across the entire cluster. + +**Characteristics:** + +- Full cluster-wide access to manage MCPServers in any namespace +- Uses `ClusterRole` and `ClusterRoleBinding` for broad permissions +- Simplest configuration and management +- Best for single-tenant clusters or trusted environments + +To explicitly configure cluster mode, include the following property in your +Helm `values.yaml` file: + +```yaml title="values.yaml" +operator: + rbac: + scope: 'cluster' +``` + +Reference the `values.yaml` file when you install the operator using Helm: + +```bash {3} +helm upgrade --install toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \ + -n toolhive-system --create-namespace + -f values.yaml +``` + +This is the default configuration used in the standard installation commands. + +### Namespace mode + +Namespace mode restricts the operator's access to only specified namespaces. +This mode is perfect for multi-tenant environments and organizations following +the principle of least privilege. + +**Characteristics:** + +- Restricted access to only specified namespaces +- Uses `ClusterRole` with namespace-specific `RoleBindings` for precise access + control +- Enhanced security through reduced blast radius +- Ideal for multi-tenant environments and compliance requirements + +To configure namespace mode, include the following in your Helm `values.yaml`: + +```yaml title="values.yaml" +operator: + rbac: + scope: 'namespace' + allowedNamespaces: + - 'team-frontend' + - 'team-backend' + - 'staging' + - 'production' +``` + +This example lets the operator manage MCPServer resources in the four namespaces +listed in the `allowedNamespaces` property. Adjust the list to match your +environment. + +Reference the `values.yaml` file when you install the operator using Helm: + +```bash {3} +helm upgrade --install toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator \ + -n toolhive-system --create-namespace + -f values.yaml +``` + +Verify the RoleBindings are created: + +```bash +kubectl get rolebinding --all-namespaces | grep toolhive +``` + +You should see RoleBindings in the specified namespaces, granting the operator +access to manage MCPServers. Example output: + +```text +NAMESPACE NAME ROLE +team-frontend toolhive-operator-manager-rolebinding ClusterRole/toolhive-operator-manager-role +team-backend toolhive-operator-manager-rolebinding ClusterRole/toolhive-operator-manager-role +staging toolhive-operator-manager-rolebinding ClusterRole/toolhive-operator-manager-role +production toolhive-operator-manager-rolebinding ClusterRole/toolhive-operator-manager-role +toolhive-system toolhive-operator-leader-election-rolebinding Role/toolhive-operator-leader-election-role +``` + +### Migrate between modes + +You can switch between cluster mode and namespace mode by updating the +`values.yaml` file and reapplying the Helm chart as shown above. Migration in +both directions is supported. + +## Check operator status + +To verify the operator is working correctly: + +```bash +# Verify CRDs are installed +kubectl get crd | grep toolhive + +# Check operator deployment status +kubectl get deployment -n toolhive-system toolhive-operator + +# Check operator service account and RBAC +kubectl get serviceaccount -n toolhive-system +kubectl get clusterrole | grep toolhive +kubectl get clusterrolebinding | grep toolhive + +# Check operator pod status +kubectl get pods -n toolhive-system +# Check operator pod logs +kubectl logs -n toolhive-system +``` + +## Upgrade the operator + +To upgrade the ToolHive operator to a new version, you need to upgrade both the +CRDs and the operator installation. + +### Upgrade the CRDs + +Choose an upgrade method based on your needs: + +- **Helm** (recommended): Provides customization options and manages the full + lifecycle of the operator. CRDs are installed and upgraded automatically as + part of the Helm chart. +- **kubectl**: Uses static manifests for a simple installation. Useful for + environments where Helm isn't available or for GitOps workflows. + +![Latest CRD Helm chart release](https://img.shields.io/github/v/release/stacklok/toolhive?style=for-the-badge&logo=helm&label=Latest%20CRD%20chart&color=bddfc2) + + + + +To upgrade the ToolHive operator to a new version, upgrade the CRDs first by +upgrading with the desired CRDs chart: + +```bash +helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds --version 0.12.1 +``` + + + + +To upgrade the CRDs using `kubectl`, run the following, ensuring you only apply +the CRDs you need for the features you want: + +```bash +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_embeddingservers.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpexternalauthconfigs.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcptoolconfigs.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpremoteproxies.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpservers.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpgroups.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_mcpregistries.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_virtualmcpcompositetooldefinitions.yaml +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.12.1/deploy/charts/operator-crds/crds/toolhive.stacklok.dev_virtualmcpservers.yaml +``` + + + + +Replace `v0.12.1` in the commands above with your target CRD version. + +### Upgrade the operator Helm release + +Then, upgrade the operator installation using Helm. + +![Latest Operator Helm chart release](https://img.shields.io/github/v/release/stacklok/toolhive?style=for-the-badge&logo=helm&label=Latest%20Operator%20chart&color=bddfc2) + +```bash +helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator -n toolhive-system +``` + +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 --version 0.12.1 +``` + +If you have a custom `values.yaml` file, include it with the `-f` flag: + +```bash +helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator -n toolhive-system -f values.yaml +``` + +## Uninstall the operator + +To uninstall the operator and CRDs: + +First, uninstall the operator: + +```bash +helm uninstall toolhive-operator -n toolhive-system +``` + +Then, if you want to completely remove ToolHive including all CRDs and related +resources, delete the CRDs. + +:::warning + +This will delete all MCPServer and related resources in your cluster! + +::: + + + + +```bash +helm uninstall toolhive-operator-crds +``` + +:::note + +If you installed the CRDs with Helm and have `crds.keep` still set to `true`, +first upgrade the chart with `--set crds.keep=false` so that when you uninstall +the CRDs chart, it completely removes all CRDs too: + +```bash +helm upgrade toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds --set crds.keep=false +``` + +::: + + + + +To remove the CRDs using `kubectl`, run the following: + +```bash +kubectl delete crd embeddingservers.toolhive.stacklok.dev +kubectl delete crd mcpexternalauthconfigs.toolhive.stacklok.dev +kubectl delete crd mcptoolconfigs.toolhive.stacklok.dev +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 +``` + + + + +If you created the `toolhive-system` namespace with Helm's `--create-namespace` +flag, delete it manually: + +```bash +kubectl delete namespace toolhive-system +``` + +## Next steps + +- [Run MCP servers in Kubernetes](./run-mcp-k8s.mdx) to create and manage MCP + servers using the ToolHive operator +- [Configure authentication](./auth-k8s.mdx) before exposing servers externally + +## Related information + +- [Kubernetes introduction](./intro.mdx) - Overview of ToolHive's Kubernetes + integration +- [ToolHive operator tutorial](./quickstart.mdx) - Step-by-step tutorial for + getting started using a local kind cluster + +## Troubleshooting + +
+Authentication error with ghcr.io + +If you encounter an authentication error when pulling the Helm chart, it might +indicate a problem with your access to the GitHub Container Registry +(`ghcr.io`). + +ToolHive's charts and images are public, but if you've previously logged into +`ghcr.io` using a personal access token, you might need to re-authenticate if +your token has expired or been revoked. + +See the GitHub documentation to +[re-authenticate to the registry](https://docs.github.com/en/packages/working-with-a-github-packages-registry/working-with-the-container-registry#authenticating-with-a-personal-access-token-classic). + +
+ +
+Operator pod fails to start + +If the operator pod is not starting or is in a `CrashLoopBackOff` state, check +the pod logs for error messages: + +```bash +kubectl get pods -n toolhive-system +# Note the name of the toolhive-operator pod + +kubectl describe pod -n toolhive-system +kubectl logs -n toolhive-system +``` + +Common causes include: + +- **Missing CRDs**: Ensure the CRDs were installed successfully before + installing the operator. The operator requires the CRDs to function properly. +- **Configuration errors**: Check your `values.yaml` file for any + misconfigurations +- **Insufficient permissions**: Ensure your cluster has the necessary RBAC + permissions for the operator to function +- **Resource constraints**: Check if the cluster has sufficient CPU and memory + resources available +- **Image pull issues**: Verify that the cluster can pull images from `ghcr.io` + +
+ +
+CRD upgrade fails with namespace mismatch + +If you see an error like the following when upgrading the CRD chart: + +```text +Error: invalid ownership metadata; annotation validation error: +key "meta.helm.sh/release-namespace" must equal "toolhive-system": +current value is "default" +``` + +This means the CRD chart was originally installed in a different namespace than +the one you're now targeting. To fix this, patch the +`meta.helm.sh/release-namespace` annotation on all CRDs to match your desired +namespace: + +```bash +for crd in $(kubectl get crd -o name | grep toolhive.stacklok.dev); do + kubectl annotate "$crd" \ + meta.helm.sh/release-namespace= --overwrite +done +``` + +Replace `` with the namespace you want to use going forward +(for example, `toolhive-system`). This is a one-time operation. After patching, +future upgrades work as long as you use the same namespace consistently. + +
+ +
+CRDs installation fails + +If the CRDs installation fails, you might see errors about existing resources or +permission issues: + +```bash +# Check if CRDs already exist +kubectl get crd | grep toolhive + +# Remove existing CRDs if needed (this will delete all related resources) +kubectl delete crd +``` + +To reinstall the CRDs: + +```bash +helm uninstall toolhive-operator-crds +helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds +``` + +
+ +
+Namespace creation issues + +If you encounter permission errors when creating the `toolhive-system` +namespace, create it manually first: + +```bash +kubectl create namespace toolhive-system +``` + +Then install the operator without the `--create-namespace` flag: + +```bash +helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator -n toolhive-system +``` + +
+ +
+Helm chart not found + +If Helm cannot find the chart, ensure you're using the correct OCI registry URL +and that your Helm version supports OCI registries (v3.8.0+): + +```bash +# Check Helm version +helm version + +# Try pulling the chart explicitly +helm pull oci://ghcr.io/stacklok/toolhive/toolhive-operator +``` + +
+ +
+Network connectivity issues + +If you're experiencing network timeouts or connection issues: + +- Verify your cluster has internet access to reach `ghcr.io` +- Check if your organization uses a proxy or firewall that might block access +- Consider using a private registry mirror if direct access is restricted + +
diff --git a/versioned_docs/version-1.1/toolhive/guides-k8s/index.mdx b/versioned_docs/version-1.1/toolhive/guides-k8s/index.mdx new file mode 100644 index 00000000..0c9957b6 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-k8s/index.mdx @@ -0,0 +1,35 @@ +--- +title: Using the ToolHive Kubernetes Operator +description: + How-to guides for using the ToolHive Kubernetes Operator to run and manage MCP + servers. +--- + +import DocCardList from '@theme/DocCardList'; + +## Introduction + +The ToolHive Kubernetes Operator manages MCP servers in Kubernetes clusters. You +define MCP servers as Kubernetes custom resources and the operator automates +their deployment, proxying, and lifecycle management. The operator supports +three resource types: + +- **MCPServer** — run a containerized MCP server inside your cluster +- **MCPRemoteProxy** — proxy an MCP server hosted outside your cluster +- **VirtualMCPServer** — aggregate multiple servers behind a single endpoint + +## Where to start + +- **New to ToolHive on Kubernetes?** Start with the + [Quickstart](./quickstart.mdx) to deploy your first MCP server on a local kind + cluster. +- **Operator already installed?** Jump to [Run MCP servers](./run-mcp-k8s.mdx), + [Proxy remote MCP servers](./remote-mcp-proxy.mdx), or set up a + [Virtual MCP Server](../guides-vmcp/index.mdx). +- **Not sure which resource type to use?** See the + [decision guide](./intro.mdx#which-resource-type-should-i-use) in the + Introduction. + +## Contents + + diff --git a/versioned_docs/version-1.1/toolhive/guides-k8s/intro.mdx b/versioned_docs/version-1.1/toolhive/guides-k8s/intro.mdx new file mode 100644 index 00000000..1e144093 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-k8s/intro.mdx @@ -0,0 +1,101 @@ +--- +title: Introduction +description: How to manage MCP servers in Kubernetes with the ToolHive Kubernetes Operator +--- + +The ToolHive Kubernetes Operator manages MCP servers in Kubernetes clusters. It +lets you define MCP servers as Kubernetes resources and automates their +deployment and management. + +:::info + +See the [ToolHive Operator quickstart tutorial](./quickstart.mdx) to get started +quickly using a local kind cluster. Try it out and +[share your feedback](https://discord.gg/stacklok)! + +::: + +## How the operator works + +The operator introduces new Custom Resource Definitions (CRDs) into your +Kubernetes cluster. The primary CRDs for MCP server workloads are `MCPServer`, +which represents a single MCP server running in Kubernetes, `MCPRemoteProxy`, +which represents an MCP server running outside the cluster that is proxied by +ToolHive, and `VirtualMCPServer`, which represents a virtual MCP server gateway +that aggregates multiple backend MCP servers. + +All ToolHive CRDs are registered under the `toolhive` category, so you can list +every ToolHive resource in your cluster with a single command: + +```bash +kubectl get toolhive -n toolhive-system +``` + +When you create an `MCPServer` resource, the operator automatically: + +1. Creates a Deployment to run the MCP server +2. Sets up a Service to expose the MCP server +3. Configures the appropriate permissions and settings +4. Manages the lifecycle of the MCP server + +```mermaid +flowchart TB + subgraph K8s["

Kubernetes cluster

"] + subgraph K8s1["**Deployment**"] + Svc1["HTTP Proxy
Service"] -- http --> Proxy1["HTTP Proxy
Pod"] -- stdio or http --> MCP1["MCP Server
Pod"] + Proxy1 -.->|creates| MCP1 + end + subgraph K8s2["**Deployment**"] + Svc2["HTTP Proxy
Service"] -- http --> Proxy2["HTTP Proxy
Pod"] -- stdio or http --> MCP2["MCP Server
Pod"] + Proxy2 -.->|creates| MCP2 + end + Ingress["Ingress"] -- http --> Svc1 & Svc2 + Operator["ToolHive
Operator"] -.->|creates| K8s1 & K8s2 + end + + Client["MCP Client
[ex: Copilot]"] -- http --> Ingress +``` + +`MCPRemoteProxy` and `VirtualMCPServer` resources work similarly, with the +operator managing a proxy pod that connects to remote MCP servers or aggregates +multiple backends, respectively. + +The diagram shows how clients connect to MCP servers through a standard Ingress +or Gateway. To learn how to expose your MCP servers and connect clients, see +[Connect clients to MCP servers](./connect-clients.mdx). + +## Which resource type should I use? + +The operator introduces three resource types for MCP workloads. Choose based on +where your MCP server runs and how many servers you need to manage: + +| Resource | Use when | +| -------------------- | ----------------------------------------------------------------------------------------------------------------- | +| **MCPServer** | Running an MCP server as a container inside your cluster | +| **MCPRemoteProxy** | Connecting to an MCP server hosted outside your cluster (SaaS tools, external APIs, remote endpoints) | +| **VirtualMCPServer** | Aggregating multiple MCPServer and/or MCPRemoteProxy resources behind a single endpoint for a team or application | + +Most teams start with `MCPServer` for container-based servers, add +`MCPRemoteProxy` for external SaaS tools, and graduate to `VirtualMCPServer` +when managing five or more servers or needing centralized authentication. + +The operator also provides shared configuration CRDs that you reference from +workload resources: + +| Resource | Purpose | +| ------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------- | +| [**MCPOIDCConfig**](./auth-k8s.mdx#set-up-shared-oidc-configuration-with-mcpoidcconfig) | Shared OIDC authentication settings, referenced via `oidcConfigRef` | +| [**MCPTelemetryConfig**](./telemetry-and-metrics.mdx#shared-telemetry-configuration-recommended) | Shared telemetry/observability settings, referenced via `telemetryConfigRef` | +| [**MCPToolConfig**](./customize-tools.mdx) | Tool filtering and renaming, referenced via `toolConfigRef` | +| [**MCPExternalAuthConfig**](./auth-k8s.mdx#set-up-embedded-authorization-server-authentication) | Token exchange or embedded auth server configuration, referenced via `externalAuthConfigRef` | + +## Installation + +[Deploy the ToolHive operator](./deploy-operator.mdx) in your Kubernetes +cluster. + +Once the operator is installed, you can create and manage MCP servers: + +- [Run MCP servers in Kubernetes](./run-mcp-k8s.mdx) +- [Proxy remote MCP servers](./remote-mcp-proxy.mdx) +- [Virtual MCP Server (vMCP)](../guides-vmcp/index.mdx) diff --git a/versioned_docs/version-1.1/toolhive/guides-k8s/logging.mdx b/versioned_docs/version-1.1/toolhive/guides-k8s/logging.mdx new file mode 100644 index 00000000..7a7bbb07 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-k8s/logging.mdx @@ -0,0 +1,411 @@ +--- +title: Audit logging +description: Configure and manage logging for ToolHive in Kubernetes environments +--- + +ToolHive provides structured JSON logging for MCP servers in Kubernetes, giving +you detailed operational insights and compliance audit trails. You can configure +log levels, enable audit logging for tracking MCP operations, and integrate with +common log collection systems like Fluentd, Filebeat, and Splunk. + +## Overview + +The ToolHive operator provides two types of logs: + +1. **Standard application logs** - Structured operational logs from the ToolHive + operator and proxy components +2. **Audit logs** - Security and compliance logs tracking all MCP operations + +```mermaid +flowchart TB + subgraph Sources["Log sources"] + Op["ToolHive operator"] + Proxy["HTTP proxy pods"] + MCP["MCP server pods"] + end + + subgraph Processing["Log processing"] + Stdout["Standard output
(structured JSON)"] + Audit["Audit logger
(structured JSON)"] + end + + subgraph Destinations["Log destinations"] + K8s["Kubernetes logs"] + ELK["ELK stack"] + Splunk["Splunk"] + Datadog["Datadog"] + Etc["...other collectors"] + end + + Op --> Stdout + Proxy --> Stdout & Audit + MCP --> Stdout + + Stdout --> K8s + Audit --> K8s + + K8s --> ELK & Splunk & Datadog & Etc +``` + +## Structured application logs + +ToolHive automatically outputs structured JSON logs to the standard output +(stdout) of the operator and HTTP proxy (`proxyrunner`) pods. + +All logs use a consistent format for easy parsing by log collectors: + +```json +{ + "level": "info", + "ts": 1761934317.963125, + "caller": "logger/logger.go:39", + "msg": "MCP server github started successfully" +} +``` + +### Key fields in application logs + +| Field | Type | Description | +| -------- | ------ | ------------------------------------------------ | +| `level` | string | Log level: `debug`, `info`, `warn`, `error` | +| `ts` | float | Unix timestamp with microseconds | +| `caller` | string | Source file and line number of the log statement | +| `msg` | string | Log message (exact content varies by event) | + +## Enable audit logging + +Audit logs provide detailed records of all MCP operations for security and +compliance. To enable audit logging, set the `audit.enabled` field to `true` in +your MCP server manifest: + + + + +```yaml {11-12} +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: + namespace: toolhive-system +spec: + image: + # ... other spec fields ... + + # Enable audit logging + audit: + enabled: true +``` + + + + +```yaml {11-12} +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRemoteProxy +metadata: + name: + namespace: toolhive-system +spec: + remoteUrl: + # ... other spec fields ... + + # Enable audit logging + audit: + enabled: true +``` + + + + +```yaml {11-14} +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: + namespace: toolhive-system +spec: + config: + groupRef: + + # Enable audit logging + audit: + enabled: true + includeRequestData: true + includeResponseData: true + + # ... other configs ... +``` + + + + +ToolHive writes audit logs to stdout alongside standard application logs. Your +log collector can differentiate them using the `audit_id` field or by filtering +for `"msg": "audit_event"`. + +### Audit log format + +When audit logging is enabled, each MCP operation generates a structured audit +event. For example, here is a sample audit log entry for a tool execution +request from an MCPServer resource: + +```json +{ + "time": "2024-01-01T12:00:00.123456789Z", + "level": "INFO+2", + "msg": "audit_event", + "audit_id": "550e8400-e29b-41d4-a716-446655440000", + "type": "mcp_tool_call", + "logged_at": "2024-01-01T12:00:00.123456Z", + "outcome": "success", + "component": "github-server", + "source": { + "type": "network", + "value": "10.0.1.5", + "extra": { + "user_agent": "node" + } + }, + "subjects": { + "user": "john.doe@example.com", + "user_id": "user-123" + }, + "target": { + "endpoint": "/messages", + "method": "tools/call", + "name": "search_issues", + "type": "tool" + }, + "metadata": { + "extra": { + "duration_ms": 245, + "transport": "http" + } + } +} +``` + +:::info[User information in audit logs] + +User information in the `subjects` field comes from JWT claims when OIDC +authentication is configured. The system uses the `name`, `preferred_username`, +or `email` claim (in that order) for the display name. If authentication is not +configured, the `user_id` field is set to `local`. + +::: + +#### Key fields in audit logs + +| Field | Description | +| --------------- | --------------------------------------------- | +| `audit_id` | Unique identifier for the audit event | +| `type` | Type of MCP operation (see event types below) | +| `outcome` | Result: `success` or `failure` | +| `component` | Name of the MCP server | +| `subjects.user` | User display name (from JWT claims) | +| `target.method` | MCP method called | +| `target.name` | Tool/resource name | + +#### Common audit event types + +| Event Type | Description | +| -------------------- | ------------------------- | +| `mcp_initialize` | MCP server initialization | +| `mcp_tool_call` | Tool execution request | +| `mcp_tools_list` | List available tools | +| `mcp_resource_read` | Resource access | +| `mcp_resources_list` | List available resources | + +
+Complete audit field reference + +#### Audit log fields + +| Field | Type | Description | +| ---------------------------- | ------ | ----------------------------------------------------------------------- | +| `time` | string | Timestamp when the log was generated | +| `level` | string | Log level (INFO+2 for audit events) | +| `msg` | string | Always "audit_event" for audit logs | +| `audit_id` | string | Unique identifier for the audit event | +| `type` | string | Type of MCP operation (see event types below) | +| `logged_at` | string | UTC timestamp of the event | +| `outcome` | string | Result of the operation: `success` or `failure` | +| `component` | string | Name of the MCP server | +| `source` | object | Request source information | +| `source.type` | string | Source type (e.g., "network") | +| `source.value` | string | Source identifier (e.g., IP address) | +| `source.extra` | object | Additional source metadata | +| `subjects` | object | User and identity information | +| `subjects.user` | string | User display name (from JWT claims: name, preferred_username, or email) | +| `subjects.user_id` | string | User identifier (from JWT sub claim) | +| `subjects.client_name` | string | Client application name (optional, from JWT claims) | +| `subjects.client_version` | string | Client version (optional, from JWT claims) | +| `target` | object | Target resource information | +| `target.endpoint` | string | API endpoint path | +| `target.method` | string | MCP method called | +| `target.name` | string | Tool or resource name | +| `target.type` | string | Target type (e.g., "tool") | +| `metadata` | object | Additional metadata | +| `metadata.extra.duration_ms` | number | Operation duration in milliseconds | +| `metadata.extra.transport` | string | Transport protocol used | + +#### Audit event types + +| Event Type | Description | +| -------------------- | ------------------------- | +| `mcp_initialize` | MCP server initialization | +| `mcp_tool_call` | Tool execution request | +| `mcp_tools_list` | List available tools | +| `mcp_resource_read` | Resource access | +| `mcp_resources_list` | List available resources | +| `mcp_prompt_get` | Prompt retrieval | +| `mcp_prompts_list` | List available prompts | +| `mcp_notification` | MCP notifications | +| `mcp_ping` | Health check pings | +| `mcp_completion` | Request completion | + +
+ +## Set up log collection + +ToolHive outputs structured JSON logs that work with your existing log +collection infrastructure. The examples below show basic host-based +configurations for common log collectors. Adapt these patterns to match your +organization's logging setup. + +:::note[Prerequisites] + +These examples assume: + +- Container logs are available at `/var/log/pods/` +- You have a standard Kubernetes logging setup +- Deployment manifests are handled separately +- You're using host-based log collection + +::: + +:::tip[Use your existing collection methods] + +If your organization uses sidecar-based or operator-based log collection (such +as Fluent Bit sidecars or the Fluentd Operator), adapt these configuration +patterns to work with your existing infrastructure. + +::: + +### Configure Fluentd + +```text +# fluentd.conf + + @type tail + path /var/log/pods/*toolhive*.log + tag toolhive + read_from_head true + + @type json + time_key time + time_format %Y-%m-%dT%H:%M:%S.%NZ + + + +# Route standard logs + + @type elasticsearch + host elasticsearch.logging.svc.cluster.local + port 9200 + index_name toolhive + + +# Route audit logs (entries that contain audit_id) to a separate index + + @type grep + + key audit_id + pattern .+ + + @label @AUDIT + + + +``` + +### Configure Filebeat + +```yaml +filebeat.inputs: + - type: container + paths: + - /var/log/pods/*toolhive*.log + json.keys_under_root: true + json.add_error_key: true + +output.elasticsearch: + hosts: ['${ELASTICSEARCH_HOST:elasticsearch}:${ELASTICSEARCH_PORT:9200}'] + indices: + - index: 'toolhive-audit-%{+yyyy.MM.dd}' + when.has_fields: ['audit_id'] + - index: 'toolhive-%{+yyyy.MM.dd}' +``` + +### Configure Splunk + +```ini +# inputs.conf +[monitor:///var/log/pods/*toolhive*] +sourcetype = _json +index = toolhive + +# props.conf +[_json] +KV_MODE = json +SHOULD_LINEMERGE = false +TRANSFORMS-route_audit = route_audit + +# transforms.conf +[route_audit] +REGEX = "audit_id":\s*".+" +DEST_KEY = _MetaData:Index +FORMAT = toolhive_audit +``` + +## Security considerations + +Protect your log data by implementing appropriate access controls and +encryption: + +### Encrypt logs + +- Encrypt audit logs at rest and in transit +- Use TLS for log shipping to external systems + +### Restrict log access + +Implement RBAC to control who can access pod logs: + +```yaml +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: log-reader + namespace: toolhive-system +rules: + - apiGroups: [''] + resources: ['pods/log'] + verbs: ['get', 'list'] +``` + +## Next steps + +- Learn about [telemetry and metrics](./telemetry-and-metrics.mdx) to complement + your logging setup +- See the [observability overview](../concepts/observability.mdx) for the + complete monitoring picture +- Check the [Kubernetes CRD reference](../reference/crd-spec.md) for complete + configuration options diff --git a/versioned_docs/version-1.1/toolhive/guides-k8s/mcp-server-entry.mdx b/versioned_docs/version-1.1/toolhive/guides-k8s/mcp-server-entry.mdx new file mode 100644 index 00000000..2be428c1 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-k8s/mcp-server-entry.mdx @@ -0,0 +1,461 @@ +--- +title: Declare remote MCP server entries +description: + Register remote MCP servers as lightweight catalog entries for vMCP discovery + without deploying proxy infrastructure. +--- + +## Overview + +MCPServerEntry is a zero-infrastructure catalog entry that declares a remote MCP +server endpoint for [Virtual MCP Server (vMCP)](../guides-vmcp/index.mdx) +discovery and routing. Unlike [MCPRemoteProxy](./remote-mcp-proxy.mdx), it +creates no pods, services, or deployments. Use MCPServerEntry when you want to +include a remote server in vMCP routing without the overhead of running a proxy. + +```mermaid +flowchart LR + Client["Client"] -->|HTTP| vMCP["vMCP Gateway"] + vMCP -->|"direct (no proxy)"| RemoteMCP["Remote MCP Server"] + + subgraph K8s["Kubernetes Cluster"] + subgraph NS["toolhive-system"] + Operator["ToolHive Operator"] + vMCP + Entry["MCPServerEntry
(catalog entry only)"] + end + end + + Operator -.->|validates| Entry + vMCP -.->|discovers| Entry +``` + +MCPServerEntry is part of an +[MCPGroup](../reference/crd-spec.md#apiv1alpha1mcpgroup), which groups related +backend MCP servers together for vMCP discovery. When vMCP starts in +[discovered mode](../guides-vmcp/backend-discovery.mdx), it queries all +MCPServer, MCPRemoteProxy, and MCPServerEntry resources in the referenced group +and connects to them directly. + +## Prerequisites + +- A Kubernetes cluster (current and two previous minor versions are supported) +- Permissions to create resources in the cluster +- [`kubectl`](https://kubernetes.io/docs/tasks/tools/) configured to communicate + with your cluster +- The ToolHive operator installed in your cluster (see + [Deploy the operator](./deploy-operator.mdx)) +- A remote MCP server that supports HTTP transport (SSE or Streamable HTTP) + +## When to use MCPServerEntry vs. MCPRemoteProxy + +| | MCPServerEntry | MCPRemoteProxy | +| ------------------- | -------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | +| **Infrastructure** | No pods, services, or deployments | Creates a proxy pod and service | +| **Use case** | Lightweight catalog entries for well-known remote servers | Proxied connections requiring request transformation, caching, or the full middleware chain | +| **Discovery** | Discovered by VirtualMCPServer through MCPGroup membership | Discovered by VirtualMCPServer through MCPGroup membership | +| **Authentication** | Token exchange via `externalAuthConfigRef` | Full OIDC validation of incoming client requests | +| **Authorization** | Not applicable (no proxy layer) | Cedar policy enforcement on every request | +| **Audit logging** | Not applicable (no proxy layer) | Structured audit logs with user identity | +| **Telemetry** | Not applicable (no proxy layer) | OpenTelemetry tracing and Prometheus metrics | +| **SSRF protection** | Built-in URL validation blocks internal and metadata endpoints | N/A (proxy runs inside the cluster) | + +Choose MCPServerEntry when: + +- You trust the remote server and don't need per-request policy enforcement +- You want the simplest possible configuration with no workload resources (pods, + services, deployments) +- The remote server handles its own authentication + +Choose MCPRemoteProxy when: + +- You need to validate incoming client tokens with OIDC +- You need Cedar authorization policies on tool calls +- You need audit logging with user identity +- You need tool filtering or renaming at the proxy layer + +## Create an MCPServerEntry + +MCPServerEntry resources must be part of an MCPGroup. Create the group first if +it doesn't exist: + +```yaml title="my-group.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 +``` + +Then create a basic MCPServerEntry: + +```yaml title="my-entry.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServerEntry +metadata: + name: my-remote-tool + namespace: toolhive-system +spec: + groupRef: my-group + remoteURL: https://mcp.example.com/mcp + transport: streamable-http +``` + +Apply both resources: + +```bash +kubectl apply -f my-group.yaml -f my-entry.yaml +``` + +:::info[What's happening?] + +When you apply an MCPServerEntry resource: + +1. The ToolHive operator detects the new resource +2. The operator validates the spec: checks that the referenced MCPGroup exists, + validates the remote URL against SSRF patterns, and verifies any referenced + auth or TLS resources +3. The operator sets the entry's phase to `Valid` if all checks pass, or + `Failed` with a descriptive condition if something is wrong +4. When a VirtualMCPServer in + [discovered mode](../guides-vmcp/backend-discovery.mdx) starts, it discovers + the entry through its MCPGroup membership and connects directly to the remote + URL + +::: + +### Required fields + +| Field | Description | Validation | +| ----------- | ------------------------------------------ | -------------------------- | +| `remoteURL` | URL of the remote MCP server | Must match `^https?://` | +| `transport` | Transport protocol for the remote server | `sse` or `streamable-http` | +| `groupRef` | Name of the MCPGroup this entry belongs to | Required, minimum length 1 | + +## Configure authentication + +When the remote MCP server requires authentication, reference an +[MCPExternalAuthConfig](./token-exchange-k8s.mdx) resource to configure token +exchange. The MCPExternalAuthConfig must exist in the same namespace as the +MCPServerEntry. + +```yaml title="auth-entry.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPExternalAuthConfig +metadata: + name: my-auth-config + namespace: toolhive-system +spec: + type: tokenExchange + tokenExchange: + tokenUrl: https://auth.company.com/protocol/openid-connect/token + clientId: remote-mcp-client + clientSecretRef: + name: remote-mcp-secret + key: client-secret + audience: https://mcp.example.com +--- +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServerEntry +metadata: + name: internal-tool + namespace: toolhive-system +spec: + groupRef: my-group + remoteURL: https://internal-mcp.corp.example.com/mcp + transport: streamable-http + # highlight-next-line + externalAuthConfigRef: + name: my-auth-config +``` + +When vMCP discovers this entry, it uses the referenced MCPExternalAuthConfig to +perform token exchange before forwarding requests to the remote server. + +## Configure custom TLS certificates + +If the remote server uses a certificate signed by an internal CA, provide a +custom CA bundle so that vMCP can verify the TLS connection. + +First, create a ConfigMap containing the CA certificate: + +```bash +kubectl create configmap internal-ca-bundle \ + --from-file=ca.crt=/path/to/ca-certificate.pem \ + -n toolhive-system +``` + +Then reference it in the MCPServerEntry: + +```yaml title="tls-entry.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServerEntry +metadata: + name: internal-tool + namespace: toolhive-system +spec: + groupRef: my-group + remoteURL: https://internal-mcp.corp.example.com/mcp + transport: streamable-http + # highlight-start + caBundleRef: + configMapRef: + name: internal-ca-bundle + key: ca.crt + # highlight-end +``` + +## Inject custom headers + +Some remote MCP servers require custom headers for tenant identification, API +keys, or other purposes. Use the `headerForward` field to inject headers into +requests forwarded to the remote server. + +```yaml title="header-entry.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServerEntry +metadata: + name: my-remote-tool + namespace: toolhive-system +spec: + groupRef: my-group + remoteURL: https://mcp.example.com/mcp + transport: streamable-http + # highlight-start + headerForward: + addPlaintextHeaders: + X-Custom-Header: my-value + # highlight-end +``` + +For sensitive values like API keys, use `addHeadersFromSecret` instead. See the +[Inject custom headers](./remote-mcp-proxy.mdx#inject-custom-headers) section of +the MCPRemoteProxy guide for the full syntax, which MCPServerEntry shares. + +## Complete example + +This example creates the MCPServerEntry-related resources for authentication and +custom TLS. If you reference a CA bundle ConfigMap such as `partner-ca-bundle`, +it must already exist or be created separately: + +```yaml title="complete-entry.yaml" +--- +# 1. Create the MCPGroup +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPGroup +metadata: + name: engineering-tools + namespace: toolhive-system +spec: + description: Engineering team MCP servers + +--- +# 2. Create authentication config for token exchange +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPExternalAuthConfig +metadata: + name: remote-auth + namespace: toolhive-system +spec: + type: tokenExchange + tokenExchange: + tokenUrl: https://auth.company.com/protocol/openid-connect/token + clientId: remote-mcp-client + clientSecretRef: + name: remote-mcp-secret + key: client-secret + audience: https://mcp.partner.example.com + +--- +# 3. Create the MCPServerEntry +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServerEntry +metadata: + name: partner-tools + namespace: toolhive-system +spec: + groupRef: engineering-tools + remoteURL: https://mcp.partner.example.com/mcp + transport: streamable-http + externalAuthConfigRef: + name: remote-auth + caBundleRef: + configMapRef: + name: partner-ca-bundle + key: ca.crt + headerForward: + addPlaintextHeaders: + X-Tenant-ID: engineering +``` + +Apply all resources: + +```bash +kubectl apply -f complete-entry.yaml +``` + +## Check MCPServerEntry status + +To check the status of your entries: + +```bash +kubectl get mcpserverentries -n toolhive-system +``` + +The status shows the current phase of each entry: + +| Phase | Description | +| --------- | ------------------------------------------------------------------ | +| `Valid` | All validations passed and the entry is usable | +| `Pending` | Initial state before the first reconciliation | +| `Failed` | One or more referenced resources are missing or the URL is invalid | + +For more details about a specific entry: + +```bash +kubectl describe mcpserverentry partner-tools -n toolhive-system +``` + +Check the `Conditions` section for specific validation results: + +```bash +kubectl get mcpserverentry partner-tools -n toolhive-system -o yaml +``` + +## SSRF protection + +MCPServerEntry URLs are validated against Server-Side Request Forgery (SSRF) +patterns. The operator rejects URLs that target: + +- **Loopback addresses**: `127.0.0.0/8`, `::1` +- **Link-local addresses**: `169.254.0.0/16`, `fe80::/10` +- **Cloud metadata endpoints**: `169.254.169.254` (AWS, GCP, Azure) +- **Private network ranges**: `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16` + +If a URL fails SSRF validation, the entry's phase is set to `Failed` with a +condition describing the rejection reason. + +## Next steps + +- [Configure a VirtualMCPServer](../guides-vmcp/configuration.mdx) to aggregate + MCPServerEntry backends with other MCP servers +- [Set up backend discovery](../guides-vmcp/backend-discovery.mdx) to control + how vMCP finds and connects to backends +- [Configure authentication](../guides-vmcp/authentication.mdx) for + client-to-vMCP and vMCP-to-backend security + +## Related information + +- [MCPServerEntry CRD specification](../reference/crd-spec.md#apiv1alpha1mcpserverentry) - + Full MCPServerEntry field reference +- [Introduction to the Kubernetes Operator](./intro.mdx) - Overview of all + operator resource types +- [Proxy remote MCP servers](./remote-mcp-proxy.mdx) - Full-featured proxy for + remote MCP servers +- [Run MCP servers in Kubernetes](./run-mcp-k8s.mdx) - Deploy container-based + MCP servers + +## Troubleshooting + +
+MCPServerEntry stuck in Pending phase + +If an MCPServerEntry remains in `Pending` phase after creation: + +```bash +# Check the entry status +kubectl describe mcpserverentry -n toolhive-system + +# Check operator logs +kubectl logs -n toolhive-system -l app.kubernetes.io/name=toolhive-operator +``` + +Common causes: + +- **Operator not running**: Verify the ToolHive operator pod is healthy +- **RBAC issues**: The operator may not have permission to reconcile + MCPServerEntry resources + +
+ +
+MCPServerEntry in Failed phase + +If the entry's phase is `Failed`, check the conditions for the specific reason: + +```bash +kubectl get mcpserverentry -n toolhive-system \ + -o jsonpath='{.status.conditions}' | jq +``` + +Common causes: + +- **SSRF validation failure**: The `remoteURL` targets a blocked address range + (loopback, link-local, private network, or cloud metadata). Use an externally + routable URL +- **Missing MCPGroup**: The group referenced in `groupRef` doesn't exist. Create + the MCPGroup first +- **Missing MCPExternalAuthConfig**: The auth config referenced in + `externalAuthConfigRef` doesn't exist in the same namespace +- **Missing CA ConfigMap**: The ConfigMap referenced in `caBundleRef` doesn't + exist or the specified key is missing + +
+ +
+MCPServerEntry not appearing in vMCP backends + +If a `Valid` MCPServerEntry doesn't appear in the VirtualMCPServer's discovered +backends: + +```bash +# Verify the entry is Valid +kubectl get mcpserverentry -n toolhive-system + +# Check the VirtualMCPServer status +kubectl get virtualmcpserver -n toolhive-system \ + -o jsonpath='{.status.discoveredBackends}' | jq + +# Check vMCP pod logs +kubectl logs -n toolhive-system deployment/vmcp- +``` + +Common causes: + +- **Group mismatch**: The entry's `groupRef` doesn't match the + VirtualMCPServer's `config.groupRef` +- **vMCP not restarted**: Backend changes require a pod restart to be + discovered. Restart the vMCP deployment: + ```bash + kubectl rollout restart deployment vmcp- -n toolhive-system + ``` +- **Inline mode**: The VirtualMCPServer uses `outgoingAuth.source: inline`, + which doesn't discover backends at runtime. Switch to `discovered` mode or add + the backend explicitly to `config.backends` + +
+ +
+Remote server connection failures + +If vMCP discovers the entry but can't connect to the remote server: + +```bash +# Check vMCP logs for connection errors +kubectl logs -n toolhive-system deployment/vmcp- | grep -i error +``` + +Common causes: + +- **TLS certificate errors**: If the remote server uses an internal CA, add a + `caBundleRef` pointing to the CA certificate +- **Authentication failures**: Verify the MCPExternalAuthConfig references valid + credentials and the token exchange endpoint is reachable +- **Network policies**: Ensure egress from the vMCP pod to the remote server is + allowed +- **Transport mismatch**: Verify the `transport` field matches the remote + server's actual transport protocol + +
diff --git a/versioned_docs/version-1.1/toolhive/guides-k8s/quickstart.mdx b/versioned_docs/version-1.1/toolhive/guides-k8s/quickstart.mdx new file mode 100644 index 00000000..053107aa --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-k8s/quickstart.mdx @@ -0,0 +1,496 @@ +--- +title: 'Quickstart: ToolHive Kubernetes Operator' +sidebar_label: Quickstart +description: + Learn how to deploy the ToolHive Kubernetes Operator and use it to manage MCP + servers in a Kubernetes cluster. +--- + +import useBaseUrl from '@docusaurus/useBaseUrl'; +import ThemedImage from '@theme/ThemedImage'; + +In this tutorial, you'll learn how to deploy the ToolHive Kubernetes Operator +and use it to manage MCP servers in a Kubernetes cluster. By the end, you'll +have a working operator deployment that automatically manages MCP servers using +Kubernetes resources. + +## What you'll learn + +- How to set up a local Kubernetes cluster with kind +- How to deploy the ToolHive operator to your cluster +- How to create your first MCP server using Kubernetes resources +- How to verify that your MCP server is running correctly +- How to connect an AI agent (like GitHub Copilot) to your MCP server + +## Prerequisites + +Before starting this tutorial, make sure you have: + +- [Helm](https://helm.sh/docs/intro/install/) (v3.10 minimum, v3.14+ + recommended) installed +- [`kubectl`](https://kubernetes.io/docs/tasks/tools/) installed +- [Docker](https://docs.docker.com/get-docker/) or + [Podman](https://podman-desktop.io/downloads) installed and running +- [kind](https://kind.sigs.k8s.io/docs/user/quick-start/#installation) installed +- Basic familiarity with Kubernetes concepts (pods, deployments, services) +- An MCP client (VS Code with Copilot is used in this tutorial, but you can use + any [supported client](../reference/client-compatibility.mdx)) + +## Quickstart with Task (TL;DR) + +If you want to get up and running quickly and have +[Task](https://taskfile.dev/installation/) installed, you can use our automated +setup: + +1. Clone the ToolHive repository: + + ```bash + git clone https://github.com/stacklok/toolhive.git + cd toolhive + ``` + +2. Run the automated setup: + + ```bash + task kind-with-toolhive-operator + ``` + +This creates the kind cluster, installs an nginx ingress controller, and deploys +the latest ToolHive operator image. You should see output indicating successful +cluster creation and operator deployment. Once complete, skip to +[Step 3: Create your first MCP server](#step-3-create-your-first-mcp-server) to +continue with the tutorial. + +If you prefer to understand each step or don't have Task installed, continue +with the manual setup below. + +## Step 1: Create a kind cluster + +First, create a local Kubernetes cluster using kind. This gives you a safe +environment to experiment with the ToolHive operator. + +Create a cluster named `toolhive`: + +```bash +kind create cluster --name toolhive +``` + +Verify your cluster is running: + +```bash +kubectl cluster-info +``` + +You should see output similar to this: + +```text +Kubernetes control plane is running at https://127.0.0.1:xxxxx +CoreDNS is running at https://127.0.0.1:xxxxx/api/v1/namespaces/kube-system/services/kube-dns:dns/proxy +``` + +This confirms your cluster is running and ready for the ToolHive operator. + +:::info[What's happening?] + +Kind (Kubernetes in Docker) creates a local Kubernetes cluster using Docker +containers. This is perfect for development and testing because it's isolated +from your main system and can be easily deleted when you're done. + +::: + +## Step 2: Deploy the ToolHive operator + +Now deploy the ToolHive operator to your cluster using Helm. The operator will +watch for MCP server resources and manage their lifecycle automatically. + +First, install the operator CRDs: + +```bash +helm upgrade --install toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds +``` + +Then install the operator: + +```bash +helm upgrade --install toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator -n toolhive-system --create-namespace +``` + +Verify that the operator deployed successfully: + +```bash +kubectl get pods -n toolhive-system +``` + +You should see output similar to: + +```text +NAME READY STATUS RESTARTS AGE +toolhive-operator-xxx 1/1 Running 0 30s +``` + +If the pod shows "Running" status, your operator is ready to manage MCP servers. + +:::info[What's happening?] + +The ToolHive operator is a Kubernetes controller that watches for `MCPServer` +resources. When you create an `MCPServer` resource, the operator automatically +creates the necessary pods, services, and configurations to run that MCP server +in your cluster. + +::: + +## Step 3: Create your first MCP server + +Now for the exciting part - create an MCP server using Kubernetes resources. +You'll deploy the fetch server, which allows AI agents to retrieve web content. + +Apply the example `fetch` MCP server from the ToolHive repository: + +```bash +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/heads/main/examples/operator/mcp-servers/mcpserver_fetch.yaml +``` + +:::info[What's happening?] + +When you create an `MCPServer` resource, the ToolHive operator detects it and +automatically: + +1. Creates a deployment to run the MCP server container +2. Sets up a service to expose the server +3. Configures the necessary networking and security settings + +::: + +Check that your MCP server was created successfully: + +```bash +kubectl get mcpservers -n toolhive-system +``` + +You should see: + +```text +NAME STATUS URL AGE +fetch Running http://mcp-fetch-proxy.toolhive-system.svc.cluster.local:8080 30s +``` + +If the status is "Pending", wait a few moments and check again. If it remains +pending for a long time, see the [troubleshooting section](#troubleshooting) at +the end of this tutorial. + +## Step 4: Test your MCP server + +Verify that your MCP server is actually working. First, get the service details: + +```bash +kubectl get service mcp-fetch-proxy -n toolhive-system +``` + +Port-forward to access the service locally: + +```bash +kubectl port-forward service/mcp-fetch-proxy -n toolhive-system 8080:8080 +``` + +In another terminal, test the server: + +```bash +curl http://localhost:8080/health +``` + +You should see a response of `OK`. + +This confirms your MCP server is running and responding correctly. + +:::info[What's happening?] + +The ToolHive operator automatically creates a Kubernetes service for each MCP +server. This service provides a stable network endpoint that other applications +(like AI agents) can use to communicate with your MCP server. + +::: + +## Step 5: Connect your AI client to the MCP server + +Now that your MCP server is running in Kubernetes, connect it to an AI client +application. We'll use Visual Studio Code with GitHub Copilot as an example. + +Make sure you still have the port-forward running from Step 4. If not, restart +it in a separate terminal: + +```bash +kubectl port-forward service/mcp-fetch-proxy -n toolhive-system 8080:8080 +``` + +Configure Visual Studio Code to connect to your MCP server. Open VS Code and +access your user settings: + +1. Open the command palette (Cmd/Ctrl+Shift+P) + +2. Type "MCP: Add Server..." and select it + + + +3. Select "HTTP" as the server type + +4. Enter the server URL: `http://localhost:8080/mcp` and press Enter + +5. Enter a name for the server (e.g., "fetch") and press Enter + +6. Choose "Global" to add the server to your global settings + +7. VS Code adds the server and opens the MCP settings file. It should look like + this: + + ```json + { + "servers": { + "fetch": { + "url": "http://localhost:8080/mcp", + "type": "http" + } + }, + "inputs": [] + } + ``` + +To verify the connection, click **Start**. The indicator should change to +"Running" and show "1 tools". + + + + +Now test the connection by asking GitHub Copilot to fetch content from a +website. Open Copilot Chat in **Agent** mode and ask: "Can you fetch the content +from https://stacklok.com and summarize it for me?" + + + +GitHub Copilot should be able to use your Kubernetes-hosted MCP server to +retrieve the content and provide a summary. + + + +:::info[What's happening?] + +You're manually configuring VS Code to connect to your MCP server running in +Kubernetes. The port-forward creates a tunnel from your local machine +(port 8080) to the Kubernetes service, allowing GitHub Copilot to communicate +with the server using the Streamable HTTP protocol. + +::: + +## Step 6: Explore operator features + +Now that you have a working MCP server, explore some operator features. + +View the detailed status of your MCP server: + +```bash +kubectl describe mcpserver fetch -n toolhive-system +``` + +This shows you the current state, any events, and configuration details. + +Try updating your MCP server's resource limits by editing the resource: + +```bash +kubectl patch mcpserver fetch -n toolhive-system --type='merge' -p '{"spec":{"resources":{"limits":{"memory":"256Mi"}}}}' +``` + +You should see output confirming the patch: + +```text +mcpserver.toolhive.stacklok.dev/fetch patched +``` + +Check that the pod has been updated with the new resource limits: + +```bash +kubectl get pods -n toolhive-system -l app.kubernetes.io/instance=fetch -o jsonpath='{.items[0].spec.containers[0].resources.limits.memory}' +``` + +You should see output showing the updated memory limit: + +```text +256Mi +``` + +This demonstrates how the operator automatically updates the underlying pod when +you modify the MCPServer resource. + +## Step 7: Clean up + +When you're done experimenting, you can clean up your resources. + +Delete the MCP server: + +```bash +kubectl delete mcpserver fetch -n toolhive-system +``` + +Verify it's been removed: + +```bash +kubectl get mcpservers -n toolhive-system +``` + +You should see: + +```text +No resources found in toolhive-system namespace. +``` + +Check that the pods are also gone: + +```bash +kubectl get pods -l app.kubernetes.io/name=fetch -n toolhive-system +``` + +You should see: + +```text +No resources found in toolhive-system namespace. +``` + +:::info[What's happening?] + +When you delete an `MCPServer` resource, the operator automatically cleans up +all the associated Kubernetes resources (pods, services, etc.). This ensures no +orphaned resources are left behind. + +::: + +When you're completely finished, delete the kind cluster: + +```bash +kind delete cluster --name toolhive +``` + +:::tip[For Task users] + +If you followed the [TL;DR setup](#quickstart-with-task-tldr) using Task, you +can also run: + +```bash +task kind-destroy +``` + +This will fully remove the kind cluster and clean up all associated resources. + +::: + +## What's next? + +Congratulations! You've successfully deployed the ToolHive operator and created +your first MCP server using Kubernetes resources. You now have a working +Kubernetes environment where MCP servers are automatically managed by the +operator. + +Here are some next steps to explore: + +- Learn about + [advanced MCP server configurations](../guides-k8s/run-mcp-k8s.mdx) for + production deployments +- Learn more about [Helm deployment options](../guides-k8s/deploy-operator.mdx) + and configuration +- Integrate MCP servers with your existing Kubernetes applications +- Try deploying other MCP servers from the ToolHive registry + +:::tip[Ready to take this to production?] + +The Kubernetes operator is available in both ToolHive Community and Stacklok +Enterprise. Enterprise adds IdP integration (Okta, Entra ID), hardened +supply-chain-attested images, a Cloud UI for fleet management, and SLA-backed +support - built for teams taking MCP from proof of concept to production. + +[See what's included in Stacklok Enterprise →](../enterprise.mdx) + +::: + +## Troubleshooting + +
+Operator pod not starting + +If the operator pod isn't starting, check the logs: + +```bash +kubectl logs -n toolhive-system deployment/toolhive-operator +``` + +
+ +
+MCP server stuck in pending state + +Check the operator logs to see what's happening: + +```bash +kubectl logs -n toolhive-system deployment/toolhive-operator -f +``` + +Also check if there are any resource constraints: + +```bash +kubectl describe mcpserver fetch -n toolhive-system +``` + +
+ +
+Can't access MCP server + +Verify the service is created and has endpoints: + +```bash +kubectl get service mcp-fetch-proxy -n toolhive-system +kubectl get endpoints mcp-fetch-proxy -n toolhive-system +``` + +
diff --git a/versioned_docs/version-1.1/toolhive/guides-k8s/rate-limiting.mdx b/versioned_docs/version-1.1/toolhive/guides-k8s/rate-limiting.mdx new file mode 100644 index 00000000..cf74695f --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-k8s/rate-limiting.mdx @@ -0,0 +1,202 @@ +--- +title: Rate limiting +description: + Configure per-user and shared rate limits on MCPServer resources to prevent + noisy neighbors and protect downstream services. +--- + +Configure token bucket rate limits on MCPServer resources to control how many +tool invocations users can make. Rate limiting prevents individual users from +monopolizing shared servers and protects downstream services from traffic +spikes. + +ToolHive supports two scopes of rate limiting: + +- **Shared** limits cap total requests across all users. +- **Per-user** limits cap requests independently for each authenticated user. + +Both scopes can be applied at the server level and overridden per tool. A +request must pass all applicable limits to proceed. + +:::info[Prerequisites] + +Before you begin, ensure you have: + +- A Kubernetes cluster with the ToolHive Operator installed +- Redis deployed in your cluster — rate limiting stores token bucket counters in + Redis (see [Redis Sentinel session storage](./redis-session-storage.mdx) for + deployment instructions) +- For per-user limits: authentication enabled on the MCPServer (`oidcConfig`, + `oidcConfigRef`, or `externalAuthConfigRef`) + +If you need help with these prerequisites, see: + +- [Kubernetes quickstart](./quickstart.mdx) +- [Authentication and authorization](./auth-k8s.mdx) + +::: + +## How rate limiting works + +Rate limits use a **token bucket** algorithm. Each bucket has a capacity +(`maxTokens`) and a refill period (`refillPeriod`). The bucket starts full and +each `tools/call` request consumes one token. When the bucket is empty, requests +are rejected until tokens refill. The refill rate is `maxTokens / refillPeriod` +tokens per second. + +Only `tools/call` requests are rate-limited. Lifecycle methods (`initialize`, +`ping`) and discovery methods (`tools/list`, `prompts/list`) pass through +unconditionally. + +When a request is rejected, the proxy returns: + +- **HTTP 429** with a `Retry-After` header (seconds until a token is available) +- A **JSON-RPC error** with code `-32029` and `retryAfterSeconds` in the error + data + +If Redis is unreachable, rate limiting **fails open** and all requests are +allowed through. + +## Configure shared rate limits + +Shared limits apply a single token bucket across all users. Use them to cap +total throughput to protect downstream services. + +```yaml title="mcpserver-shared-ratelimit.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: weather-server +spec: + image: ghcr.io/stackloklabs/weather-mcp/server + transport: streamable-http + sessionStorage: + provider: redis + address: + # highlight-start + rateLimiting: + shared: + maxTokens: 1000 + refillPeriod: 1m0s + # highlight-end +``` + +This allows 1,000 total `tools/call` requests per minute across all users. + +## Configure per-user rate limits + +Per-user limits give each authenticated user their own independent token bucket. +This prevents a single user from consuming the entire server capacity. + +Per-user limits **require authentication** to be enabled. The proxy identifies +users by the `sub` claim from their JWT token. + +```yaml title="mcpserver-peruser-ratelimit.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: weather-server +spec: + image: ghcr.io/stackloklabs/weather-mcp/server + transport: streamable-http + oidcConfig: + type: inline + inline: + issuer: https://my-idp.example.com + audience: my-audience + sessionStorage: + provider: redis + address: + # highlight-start + rateLimiting: + perUser: + maxTokens: 100 + refillPeriod: 1m0s + # highlight-end +``` + +This allows each user 100 `tools/call` requests per minute independently. + +## Combine shared and per-user limits + +You can configure both scopes together. A request must pass **all** applicable +limits. This lets you set a per-user ceiling while also capping total server +throughput. + +```yaml title="mcpserver-combined-ratelimit.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: weather-server +spec: + image: ghcr.io/stackloklabs/weather-mcp/server + transport: streamable-http + oidcConfig: + type: inline + inline: + issuer: https://my-idp.example.com + audience: my-audience + sessionStorage: + provider: redis + address: + rateLimiting: + # highlight-start + shared: + maxTokens: 1000 + refillPeriod: 1m0s + perUser: + maxTokens: 100 + refillPeriod: 1m0s + # highlight-end +``` + +## Add per-tool overrides + +Individual tools can have tighter limits than the server default. Per-tool +limits are enforced **in addition to** server-level limits. + +```yaml title="mcpserver-pertool-ratelimit.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: weather-server +spec: + image: ghcr.io/stackloklabs/weather-mcp/server + transport: streamable-http + oidcConfig: + type: inline + inline: + issuer: https://my-idp.example.com + audience: my-audience + sessionStorage: + provider: redis + address: + rateLimiting: + perUser: + maxTokens: 100 + refillPeriod: 1m0s + # highlight-start + tools: + - name: expensive_search + perUser: + maxTokens: 10 + refillPeriod: 1m0s + - name: shared_resource + shared: + maxTokens: 50 + refillPeriod: 1m0s + # highlight-end +``` + +In this example: + +- Each user can make 100 total tool calls per minute. +- Each user can make at most 10 `expensive_search` calls per minute (and those + also count toward the 100 server-level limit). +- All users combined can make 50 `shared_resource` calls per minute. + +## Next steps + +- [Token exchange](./token-exchange-k8s.mdx) to configure token exchange for + upstream service authentication +- [CRD reference](../reference/crd-spec.md) for complete field definitions diff --git a/versioned_docs/version-1.1/toolhive/guides-k8s/redis-session-storage.mdx b/versioned_docs/version-1.1/toolhive/guides-k8s/redis-session-storage.mdx new file mode 100644 index 00000000..35ac2909 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-k8s/redis-session-storage.mdx @@ -0,0 +1,609 @@ +--- +title: Redis Sentinel session storage +description: + How to deploy Redis Sentinel and configure persistent session storage for the + ToolHive embedded authorization server. +--- + +Deploy Redis Sentinel and configure it as the session storage backend for the +ToolHive embedded authorization server. By default, sessions are stored in +memory, which means upstream tokens are lost when pods restart and users must +re-authenticate. Redis Sentinel provides persistent storage with automatic +master discovery, ACL-based access control, and optional failover when replicas +are configured. + +:::info[Prerequisites] + +Before you begin, ensure you have: + +- A Kubernetes cluster with the ToolHive Operator installed +- `kubectl` configured to access your cluster +- Familiarity with the + [embedded authorization server](./auth-k8s.mdx#set-up-embedded-authorization-server-authentication) + setup + +If you need help installing the ToolHive Operator, see the +[Kubernetes quickstart guide](./quickstart.mdx). + +::: + +## Deploy Redis Sentinel + +Deploy a Redis master and a three-node Sentinel cluster. The following manifests +create the Redis and Sentinel StatefulSets with ACL authentication and +persistent storage. + +Create the `redis` namespace: + +```bash +kubectl create namespace redis +``` + +Save the following manifests to a file called `redis-sentinel.yaml`. + +The ACL Secret defines a `toolhive-auth` user with permissions restricted to the +`thv:auth:*` key pattern that ToolHive uses for session data. An init container +copies the ACL file into the Redis data directory so it persists across +restarts. + +:::tip[Generate a strong ACL password] + +Generate a random password and use it in the ACL Secret and Kubernetes Secret +below: + +```bash +openssl rand -base64 32 +``` + +In the ACL entry, the `>` prefix before the password is +[Redis ACL syntax](https://redis.io/docs/latest/operate/oss_and_stack/management/security/acl/#acl-rules) +meaning "set this user's password." Replace `YOUR_REDIS_ACL_PASSWORD` with the +generated value. + +::: + +```yaml title="redis-sentinel.yaml — Redis master and ACL" +# --- Redis ACL Secret +apiVersion: v1 +kind: Secret +metadata: + name: redis-acl + namespace: redis +type: Opaque +stringData: + # highlight-start + users.acl: >- + user toolhive-auth on >YOUR_REDIS_ACL_PASSWORD ~thv:auth:* &* +GET +SET + +SETNX +DEL +EXISTS +EXPIRE +SADD +SREM +SMEMBERS +EVAL +MULTI +EXEC + +EVALSHA +PING + # highlight-end +--- +# --- Redis headless Service +apiVersion: v1 +kind: Service +metadata: + name: redis + namespace: redis +spec: + clusterIP: None + selector: + app: redis + ports: + - name: redis + port: 6379 +--- +# --- Redis master StatefulSet +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: redis + namespace: redis +spec: + serviceName: redis + replicas: 1 + selector: + matchLabels: + app: redis + template: + metadata: + labels: + app: redis + spec: + initContainers: + - name: init-acl + image: redis:7-alpine + command: ['cp', '/etc/redis-acl/users.acl', '/data/users.acl'] + volumeMounts: + - name: redis-acl + mountPath: /etc/redis-acl + - name: redis-data + mountPath: /data + containers: + - name: redis + image: redis:7-alpine + ports: + - containerPort: 6379 + command: + - redis-server + - --bind + - '0.0.0.0' + - --aclfile + - /data/users.acl + readinessProbe: + exec: + command: ['redis-cli', 'PING'] + initialDelaySeconds: 5 + periodSeconds: 5 + resources: + requests: + cpu: 100m + memory: 256Mi + limits: + cpu: 500m + memory: 512Mi + volumeMounts: + - name: redis-data + mountPath: /data + - name: redis-acl + mountPath: /etc/redis-acl + readOnly: true + volumes: + - name: redis-acl + secret: + secretName: redis-acl + volumeClaimTemplates: + - metadata: + name: redis-data + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi +``` + +The next section deploys a three-node Sentinel cluster that monitors the Redis +master and handles automatic failover: + +```yaml title="redis-sentinel.yaml — Sentinel cluster (append to same file)" +# --- Sentinel configuration +apiVersion: v1 +kind: ConfigMap +metadata: + name: redis-sentinel-config + namespace: redis +data: + sentinel.conf: | + sentinel resolve-hostnames yes + sentinel announce-hostnames yes + # quorum: 2 of 3 sentinels must agree to trigger failover + sentinel monitor mymaster redis-0.redis.redis.svc.cluster.local 6379 2 + sentinel down-after-milliseconds mymaster 5000 + sentinel failover-timeout mymaster 10000 + sentinel parallel-syncs mymaster 1 +--- +# --- Sentinel headless Service +apiVersion: v1 +kind: Service +metadata: + name: redis-sentinel + namespace: redis +spec: + clusterIP: None + selector: + app: redis-sentinel + ports: + - name: sentinel + port: 26379 +--- +# --- Sentinel StatefulSet (3 replicas for quorum) +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: redis-sentinel + namespace: redis +spec: + serviceName: redis-sentinel + replicas: 3 + selector: + matchLabels: + app: redis-sentinel + template: + metadata: + labels: + app: redis-sentinel + spec: + initContainers: + - name: copy-config + image: redis:7-alpine + command: + ['cp', '/etc/sentinel-ro/sentinel.conf', '/data/sentinel.conf'] + volumeMounts: + - name: sentinel-config-ro + mountPath: /etc/sentinel-ro + - name: sentinel-data + mountPath: /data + containers: + - name: sentinel + image: redis:7-alpine + ports: + - containerPort: 26379 + name: sentinel + command: ['redis-sentinel', '/data/sentinel.conf'] + readinessProbe: + exec: + command: ['redis-cli', '-p', '26379', 'PING'] + initialDelaySeconds: 5 + periodSeconds: 5 + resources: + requests: + cpu: 100m + memory: 128Mi + limits: + cpu: 200m + memory: 256Mi + volumeMounts: + - name: sentinel-data + mountPath: /data + volumes: + - name: sentinel-config-ro + configMap: + name: redis-sentinel-config + volumeClaimTemplates: + - metadata: + name: sentinel-data + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 100Mi +``` + +Apply the manifests and wait for all pods to be ready: + +```bash +kubectl apply -f redis-sentinel.yaml +``` + +```bash +kubectl wait --for=condition=ready pod \ + -l 'app in (redis, redis-sentinel)' \ + --namespace redis \ + --timeout=300s +``` + +:::warning + +The manifests above don't disable the Redis default user, which has full access +with no password. For production deployments, add `user default off` to the +`users.acl` entry in the `redis-acl` Secret. If you disable the default user, +you must also configure Sentinel to authenticate to Redis by adding +`sentinel auth-user` and `sentinel auth-pass` to the Sentinel ConfigMap, and +update the readiness probe commands to authenticate. + +::: + +## Create Kubernetes secrets + +Create a Secret in the ToolHive namespace containing the Redis ACL credentials. +The username and password must match the ACL user defined above: + +```bash +kubectl create secret generic redis-acl-secret \ + --namespace toolhive-system \ + --from-literal=username=toolhive-auth \ + --from-literal=password="YOUR_REDIS_ACL_PASSWORD" +``` + +## Configure MCPExternalAuthConfig + +Add the `storage` block to your `MCPExternalAuthConfig` resource. The following +example shows a working configuration with Redis Sentinel storage using Sentinel +service discovery, which automatically resolves Sentinel endpoints from the +headless Service deployed above: + +```yaml title="embedded-auth-with-redis.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPExternalAuthConfig +metadata: + name: embedded-auth-server + namespace: toolhive-system +spec: + type: embeddedAuthServer + embeddedAuthServer: + issuer: 'https://mcp.example.com' + signingKeySecretRefs: + - name: auth-server-signing-key + key: signing-key + hmacSecretRefs: + - name: auth-server-hmac-secret + key: hmac-key + # highlight-start + storage: + type: redis + redis: + sentinelConfig: + masterName: mymaster + sentinelService: + name: redis-sentinel + namespace: redis + aclUserConfig: + usernameSecretRef: + name: redis-acl-secret + key: username + passwordSecretRef: + name: redis-acl-secret + key: password + # highlight-end + upstreamProviders: + - name: google + type: oidc + oidcConfig: + issuerUrl: 'https://accounts.google.com' + clientId: '' + clientSecretRef: + name: upstream-idp-secret + key: client-secret + scopes: + - openid + - profile + - email +``` + +```bash +kubectl apply -f embedded-auth-with-redis.yaml +``` + +### Using explicit Sentinel addresses + +:::note + +`sentinelAddrs` and `sentinelService` are mutually exclusive. Use +`sentinelService` when your Sentinel instances run in the same cluster, or +`sentinelAddrs` when you need to specify exact endpoints. + +::: + +Instead of service discovery, you can list Sentinel addresses explicitly. This +is useful when Sentinel instances are in a different namespace or outside the +cluster: + +```yaml title="storage block with sentinelAddrs" +storage: + type: redis + redis: + sentinelConfig: + masterName: mymaster + sentinelAddrs: + - redis-sentinel-0.redis-sentinel.redis.svc.cluster.local:26379 + - redis-sentinel-1.redis-sentinel.redis.svc.cluster.local:26379 + - redis-sentinel-2.redis-sentinel.redis.svc.cluster.local:26379 + aclUserConfig: + usernameSecretRef: + name: redis-acl-secret + key: username + passwordSecretRef: + name: redis-acl-secret + key: password +``` + +For the complete list of storage configuration fields, see the +[Kubernetes CRD reference](../reference/crd-spec.md#apiv1alpha1authserverstorageconfig). + +## Enable TLS + +Without TLS, Redis credentials and session tokens travel in plaintext between +ToolHive and Redis. You should enable TLS for any deployment beyond local +development. + +Configure the `tls` block in your storage config. ToolHive needs the CA +certificate that signed the Redis server certificate so it can verify the +connection. + +:::note + +This step only covers the ToolHive client-side TLS configuration. Your Redis and +Sentinel instances must also be configured to serve TLS — see the +[Redis TLS documentation](https://redis.io/docs/latest/operate/oss_and_stack/management/security/encryption/) +for server-side setup. + +::: + +### Create a CA certificate Secret + +Store your CA certificate in a Secret in the ToolHive namespace: + +```bash +kubectl create secret generic redis-ca-cert \ + --namespace toolhive-system \ + --from-file=ca.crt= +``` + +### Configure TLS in MCPExternalAuthConfig + +Add the `tls` block to the `redis` section of your storage config: + +```yaml title="storage block with TLS" +storage: + type: redis + redis: + sentinelConfig: + masterName: mymaster + sentinelService: + name: redis-sentinel + namespace: redis + aclUserConfig: + usernameSecretRef: + name: redis-acl-secret + key: username + passwordSecretRef: + name: redis-acl-secret + key: password + # highlight-start + tls: + caCertSecretRef: + name: redis-ca-cert + key: ca.crt + # highlight-end +``` + +When you set only `tls`, ToolHive automatically uses the same TLS configuration +for Sentinel connections. This is the recommended setup when both Redis and +Sentinel use certificates from the same CA. + +### Separate TLS config for Sentinel + +If your Sentinel instances use a different CA or require different TLS settings, +add a `sentinelTls` block: + +```yaml title="storage block with separate Sentinel TLS" +storage: + type: redis + redis: + sentinelConfig: + masterName: mymaster + sentinelService: + name: redis-sentinel + namespace: redis + aclUserConfig: + usernameSecretRef: + name: redis-acl-secret + key: username + passwordSecretRef: + name: redis-acl-secret + key: password + # highlight-start + tls: + caCertSecretRef: + name: redis-ca-cert + key: ca.crt + sentinelTls: + caCertSecretRef: + name: sentinel-ca-cert + key: ca.crt + # highlight-end +``` + +When `sentinelTls` is set, ToolHive uses separate TLS configurations for master +and Sentinel connections. Each connection type uses its own CA certificate for +verification. + +## Verify the integration + +After applying the configuration, verify that ToolHive can connect to Redis. The +examples below use `weather-server-embedded` as the MCPServer name — substitute +your own. + +Check that the MCPServer pod is running: + +```bash +kubectl get pods -n toolhive-system \ + -l app.kubernetes.io/name=weather-server-embedded +``` + +Check the proxy logs for Redis connection messages: + +```bash +kubectl logs -n toolhive-system \ + -l app.kubernetes.io/name=weather-server-embedded \ + | grep -i redis +``` + +Look for log entries that confirm a successful Redis Sentinel connection. If the +connection fails, the proxy logs contain error details. + +Test the OAuth flow end-to-end by connecting with an MCP client. After +authenticating, restart the proxy pod and verify that your session persists +without requiring re-authentication: + +```bash +# Restart the proxy pod +kubectl rollout restart deployment \ + -n toolhive-system weather-server-embedded-proxy + +# Wait for the new pod to be ready +kubectl rollout status deployment \ + -n toolhive-system weather-server-embedded-proxy +``` + +If your MCP client can continue making requests without re-authenticating, Redis +session storage is working correctly. + +## Troubleshooting + +
+Connection refused or timeout errors + +- Verify the Redis Sentinel pods are running: `kubectl get pods -n redis` +- Check that the Sentinel addresses in your config match the actual pod DNS + names: `kubectl get endpoints -n redis` +- Ensure network policies allow traffic from the `toolhive-system` namespace to + the `redis` namespace +- Verify the `masterName` matches the name in your Sentinel configuration + (`mymaster` in the example manifests above) + +
+ +
+ACL authentication failures + +- Verify the Secret exists and contains the correct credentials: + `kubectl get secret redis-acl-secret -n toolhive-system -o yaml` +- Connect to Redis directly to verify the ACL user exists: + ```bash + kubectl exec -n redis redis-0 -- redis-cli ACL LIST + ``` +- Ensure the ACL user has the required permissions (`~thv:auth:*` key pattern + and the commands listed in the ACL Secret) + +
+ +
+TLS handshake or certificate errors + +- Verify the CA certificate Secret exists in the `toolhive-system` namespace: + `kubectl get secret redis-ca-cert -n toolhive-system` +- Confirm the CA certificate matches the one that signed the Redis server + certificate +- Check proxy logs for TLS-specific errors: + ```bash + kubectl logs -n toolhive-system \ + -l app.kubernetes.io/name=weather-server-embedded \ + | grep -i "tls\|x509\|certificate" + ``` +- If using self-signed certificates for testing, you can set + `insecureSkipVerify: true` to bypass verification (not recommended for + production) +- When using separate Sentinel TLS, ensure both `tls` and `sentinelTls` are + configured with the correct CA certificates for their respective services + +
+ +
+Sessions lost after Redis failover + +- Check Sentinel logs for failover events: + `kubectl logs -n redis -l app=redis-sentinel` +- Verify that the master is reachable from Sentinel: + ```bash + kubectl exec -n redis redis-sentinel-0 -- \ + redis-cli -p 26379 SENTINEL masters + ``` +- Ensure Sentinel quorum is met (at least 2 of 3 Sentinel instances must be + running) + +
+ +## Next steps + +- [Configure token exchange](./token-exchange-k8s.mdx) to let MCP servers + authenticate to backend services +- [Monitor server activity](./telemetry-and-metrics.mdx) with OpenTelemetry and + Prometheus + +## Related information + +- [Set up embedded authorization server authentication](./auth-k8s.mdx#set-up-embedded-authorization-server-authentication) +- [Backend authentication](../concepts/backend-auth.mdx) +- [Kubernetes CRD reference](../reference/crd-spec.md#apiv1alpha1authserverstorageconfig) diff --git a/versioned_docs/version-1.1/toolhive/guides-k8s/remote-mcp-proxy.mdx b/versioned_docs/version-1.1/toolhive/guides-k8s/remote-mcp-proxy.mdx new file mode 100644 index 00000000..36ab3141 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-k8s/remote-mcp-proxy.mdx @@ -0,0 +1,962 @@ +--- +title: Proxy remote MCP servers +description: + How to deploy proxies for remote MCP servers in Kubernetes using the ToolHive + operator +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Overview + +The ToolHive operator can deploy proxies for remote MCP servers that are hosted +externally. This enables you to gain centralized observability, policy +enforcement, and audit logging for external MCP services without requiring +changes to the remote servers themselves. + +Remote MCP proxies sit between your users and external MCP servers, providing: + +- **Authentication**: Validate user tokens using OIDC +- **Authorization**: Apply Cedar policies based on user identity +- **Audit logging**: Track all requests with user identity and tool information +- **Tool filtering**: Control which tools are exposed to users +- **Telemetry**: Collect metrics and traces for monitoring + +```mermaid +flowchart LR + Client["Client"] -->|HTTP with token| Proxy["ToolHive
Remote Proxy"] + Proxy -->|HTTP| RemoteMCP["Remote MCP Server"] + + subgraph K8s["Kubernetes Cluster"] + subgraph NS["toolhive-system"] + Operator["ToolHive Operator"] + end + subgraph NS2["Proxy Namespace"] + Proxy + end + end + + Operator -.->|creates| Proxy + + IDP["Identity Provider
(Keycloak, Okta, etc.)"] -.->|validates tokens| Proxy +``` + +:::info[Experimental] + +The MCPRemoteProxy CRD is still in an alpha state so breaking changes to the +spec and its capabilities are possible. + +::: + +## Prerequisites + +- A Kubernetes cluster (current and two previous minor versions are supported) +- Permissions to create resources in the cluster +- [`kubectl`](https://kubernetes.io/docs/tasks/tools/) configured to communicate + with your cluster +- The ToolHive operator installed in your cluster (see + [Deploy the operator](./deploy-operator.mdx)) +- An OIDC identity provider (Keycloak, Okta, Azure AD, etc.) +- Access to a remote MCP server that supports HTTP transport (SSE or Streamable + HTTP) + +## Use cases + +### Enterprise SaaS MCP servers + +Organizations using external MCP services need centralized visibility and +control over how employees interact with these services. For example, a company +using a SaaS analytics platform with an MCP interface wants to: + +- Monitor which tools employees are calling +- Apply authorization policies based on user roles +- Maintain audit logs for compliance +- Filter or restrict access to sensitive operations +- Collect centralized telemetry + +By deploying a remote MCP proxy in Kubernetes, the company gains all these +capabilities without modifying the external service. + +### Multi-tenant access control + +Different teams within an organization may need different levels of access to +the same remote MCP server. Remote proxies enable you to: + +- Deploy separate proxies with different authorization policies +- Apply team-specific tool filters +- Maintain separate audit trails per team +- Scale proxy instances independently based on team usage + +## Create a remote MCP proxy + +You can create `MCPRemoteProxy` resources in namespaces based on how the +operator was deployed. + +- **Cluster mode (default)**: Create MCPRemoteProxy resources in any namespace +- **Namespace mode**: Create MCPRemoteProxy resources only in allowed namespaces + +See [Deploy the operator](./deploy-operator.mdx#operator-deployment-modes) to +learn about the different deployment modes. + +### Basic configuration + +This example creates a remote proxy for a fictional enterprise analytics MCP +server with OIDC authentication: + +```yaml title="analytics-proxy.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRemoteProxy +metadata: + name: analytics-proxy + namespace: toolhive-system +spec: + # Remote MCP server URL + remoteURL: https://mcp.analytics.example.com + + # Port to expose the proxy on + proxyPort: 8080 + + # Transport method (sse or streamable-http) + transport: streamable-http + + # OIDC authentication configuration + oidcConfig: + type: inline + inline: + # Your identity provider's issuer URL + issuer: https://auth.company.com/realms/production + # Expected audience claim in tokens + audience: analytics-mcp-proxy + # Optional: Client ID if using introspection + clientId: analytics-proxy + + # Authorization policies + authzConfig: + type: inline + inline: + policies: + # Allow all authenticated users to list tools + - | + permit( + principal, + action == Action::"list_tools", + resource + ); + # Allow users with 'analyst' role to call read-only tools + - | + permit( + principal, + action == Action::"call_tool", + resource + ) + when { + principal has groups && + principal.groups.contains("analyst") + }; + + # Audit logging + audit: + enabled: true + + # Resource limits + resources: + limits: + cpu: '500m' + memory: 512Mi + requests: + cpu: 100m + memory: 128Mi +``` + +Apply the resource: + +```bash +kubectl apply -f analytics-proxy.yaml +``` + +:::info[What's happening?] + +When you apply an `MCPRemoteProxy` resource, here's what happens: + +1. The ToolHive operator detects the new resource (if it's in an allowed + namespace) +2. The operator creates a Deployment running the ToolHive proxy +3. The operator creates a Service to expose the proxy +4. The proxy connects to your OIDC provider to fetch JWKS keys for token + validation +5. Clients can now connect through the proxy, which validates tokens, applies + policies, and forwards requests to the remote MCP server + +::: + +### Authentication configuration + +The `oidcConfig` field validates incoming tokens from users. The proxy supports +multiple authentication methods: + + + + +Inline OIDC configuration is the simplest approach and works with most identity +providers: + +```yaml {4-6} +spec: + remoteURL: https://mcp.example.com + + oidcConfig: + type: inline + inline: + issuer: https://auth.company.com/realms/production + audience: mcp-proxy + # Optional: For token introspection + clientId: mcp-proxy-client + clientSecretRef: + name: mcp-proxy-secret + key: client-secret +``` + +The proxy automatically discovers the JWKS URL from the issuer's OIDC discovery +endpoint (`/.well-known/openid-configuration`). + + + + +For more complex configurations or when you need to share OIDC settings across +multiple proxies, use a ConfigMap: + +```yaml {21-24} title="oidc-config.yaml" +apiVersion: v1 +kind: ConfigMap +metadata: + name: company-oidc-config + namespace: toolhive-system +data: + issuer: 'https://auth.company.com/realms/production' + audience: 'mcp-proxy' + clientId: 'mcp-proxy-client' +--- +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRemoteProxy +metadata: + name: analytics-proxy + namespace: toolhive-system +spec: + remoteURL: https://mcp.analytics.example.com + oidcConfig: + type: configMap + configMap: + name: company-oidc-config +``` + + + + +:::warning[Production security] + +For production deployments: + +- Always use HTTPS for the issuer URL +- Store client secrets in Kubernetes Secrets, not inline +- Use certificate validation (set `caBundleRef` if using custom CAs) +- Never set `insecureAllowHTTP: true` in production + +::: + +### Authorization policies + +Authorization policies are written in +[Cedar policy language](https://www.cedarpolicy.com/) and evaluated for each +request. Policies can reference claims from the validated JWT token. + +This example shows different policy patterns: + +```yaml {6-9} +spec: + remoteURL: https://mcp.example.com + oidcConfig: + # ... OIDC config ... + + authzConfig: + type: inline + inline: + policies: + # Allow all authenticated users to list tools + - | + permit( + principal, + action == Action::"list_tools", + resource + ); + + # Allow users from specific email domain + - | + permit( + principal, + action == Action::"call_tool", + resource + ) + when { + principal has email && + principal.email like "*@company.com" + }; + + # Role-based access control + - | + permit( + principal, + action == Action::"call_tool", + resource + ) + when { + principal has groups && + principal.groups.contains("admin") + }; + + # Deny destructive operations + - | + forbid( + principal, + action == Action::"call_tool", + resource + ) + when { + resource.tool in ["delete_data", "drop_table"] + }; +``` + +Available principal attributes depend on the claims in your JWT tokens. These +are examples of common JWT claims that can be referenced in policies; actual +available attributes depend on your identity provider's token configuration: + +- `sub` - Subject (User ID) +- `email` - Email address +- `name` - Display name +- `groups` - Group memberships (array) +- Custom claims from your identity provider + +### Tool filtering + +Use `MCPToolConfig` to filter or rename tools from the remote server: + +```yaml {2,31-32} title="analytics-tools.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPToolConfig +metadata: + name: analytics-tools-filter + namespace: toolhive-system +spec: + # Only expose read-only tools + toolsFilter: + - query_data + - get_report + - list_dashboards + - export_chart + + # Rename tools for clarity + toolsOverride: + query_data: + name: analytics_query_data + description: Query analytics data with custom filters + get_report: + name: analytics_get_report + description: Retrieve a generated analytics report +--- +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRemoteProxy +metadata: + name: analytics-proxy + namespace: toolhive-system +spec: + remoteURL: https://mcp.analytics.example.com + # ... other config ... + toolConfigRef: + name: analytics-tools-filter +``` + +See [Customize tools](./customize-tools.mdx) for more information about tool +filtering and renaming. + +### Token exchange + +When your organization has federation with the remote service provider, you can +configure token exchange to convert company tokens into service-specific tokens: + +First, create an `MCPExternalAuthConfig`: + +```yaml title="external-auth-config.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPExternalAuthConfig +metadata: + name: analytics-token-exchange + namespace: toolhive-system +spec: + type: tokenExchange + tokenExchange: + tokenURL: https://auth.company.com/protocol/openid-connect/token + clientID: token-exchange-client + clientSecretRef: + name: token-exchange-creds + key: client-secret + audience: https://mcp.analytics.example.com + scopes: + - 'analytics:read' + - 'analytics:write' +``` + +Then reference it in your proxy: + +```yaml {11-12} +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRemoteProxy +metadata: + name: analytics-proxy + namespace: toolhive-system +spec: + remoteURL: https://mcp.analytics.example.com + oidcConfig: + # ... Company IDP config ... + + externalAuthConfigRef: + name: analytics-token-exchange + + # ... other config ... +``` + +Now the proxy exchanges validated company tokens for remote service tokens +before forwarding requests. + +:::tip[AWS services] + +For AWS services like the AWS MCP Server, use `type: awsSts` instead of +`type: tokenExchange`. This exchanges OIDC tokens for temporary AWS credentials +via `AssumeRoleWithWebIdentity` and signs requests with SigV4. See the +[AWS STS integration tutorial](../integrations/aws-sts.mdx) for details. + +::: + +### Inject custom headers + +Some remote MCP servers require custom headers for tenant identification, API +keys, or other purposes. Use the `headerForward` field to inject headers into +every request forwarded to the remote server. + +For non-sensitive values like tenant IDs or correlation headers, use +`addPlaintextHeaders`: + +```yaml {6-9} +spec: + remoteURL: https://mcp.analytics.example.com + # ... other config ... + + headerForward: + addPlaintextHeaders: + X-Tenant-ID: 'tenant-123' + X-Correlation-ID: 'corr-abc-def-456' +``` + +For sensitive values like API keys, reference Kubernetes Secrets using +`addHeadersFromSecret`. First, create a Secret containing the header value: + +```yaml title="api-key-secret.yaml" +apiVersion: v1 +kind: Secret +metadata: + name: api-key-secret + namespace: toolhive-system +type: Opaque +stringData: + api-key: 'your-api-key-value' +``` + +Then reference the Secret in your MCPRemoteProxy: + +```yaml title="analytics-proxy.yaml" {12-17} +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRemoteProxy +metadata: + name: analytics-proxy + namespace: toolhive-system +spec: + remoteURL: https://mcp.analytics.example.com + # ... other config ... + + headerForward: + addHeadersFromSecret: + - headerName: 'X-API-Key' + valueSecretRef: + name: api-key-secret + key: api-key +``` + +You can combine plaintext and secret-backed headers: + +```yaml {6-14} +spec: + remoteURL: https://mcp.analytics.example.com + # ... other config ... + + headerForward: + addPlaintextHeaders: + X-Tenant-ID: 'tenant-123' + X-Request-Source: 'toolhive-proxy' + addHeadersFromSecret: + - headerName: 'X-API-Key' + valueSecretRef: + name: api-key-secret + key: api-key +``` + +:::warning[Security considerations] + +- Plaintext header values are visible when you inspect the full resource (e.g., + `kubectl get ... -o yaml` or `kubectl describe`). For sensitive values (API + keys, tokens), always use `addHeadersFromSecret`. +- Secret-backed header values are stored in Kubernetes Secrets and resolved at + runtime. Only secret references (not actual values) appear in ConfigMaps used + internally by ToolHive. +- Certain headers cannot be configured for security reasons, including `Host`, + `Connection`, `Transfer-Encoding`, and proxy-related headers like + `X-Forwarded-For`. + +::: + +## Quick start example + +For testing and development, you can use the public MCP specification server: + +```yaml title="mcp-spec-proxy.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRemoteProxy +metadata: + name: mcp-spec-proxy + namespace: toolhive-system +spec: + remoteURL: https://modelcontextprotocol.io/mcp + proxyPort: 8080 + transport: streamable-http + + # For testing - use your IDP's configuration + oidcConfig: + type: inline + inline: + issuer: https://auth.company.com/realms/test + audience: mcp-test + + # Simple allow-all policy for testing + authzConfig: + type: inline + inline: + policies: + - | + permit( + principal, + action, + resource + ); + + audit: + enabled: true + + resources: + limits: + cpu: '100m' + memory: 128Mi + requests: + cpu: '50m' + memory: 64Mi +``` + +Apply it and test: + +```bash +kubectl apply -f mcp-spec-proxy.yaml + +# Check status +kubectl get mcpremoteproxy -n toolhive-system + +# Port-forward to test locally +kubectl port-forward -n toolhive-system svc/mcp-mcp-spec-proxy-remote-proxy 8080:8080 + +# Get a token from your IDP and test +curl -X POST http://localhost:8080/mcp \ + -H "Authorization: Bearer " \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' +``` + +## Expose the proxy externally + +To make the proxy accessible from outside the cluster, create an Ingress +resource: + +```yaml title="proxy-ingress.yaml" +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: analytics-proxy-ingress + namespace: toolhive-system + annotations: + # Preserve the path when forwarding + nginx.ingress.kubernetes.io/rewrite-target: / +spec: + ingressClassName: nginx + rules: + - host: analytics-mcp.company.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: mcp-analytics-proxy-remote-proxy + port: + number: 8080 + # Optional: TLS configuration + tls: + - hosts: + - analytics-mcp.company.com + secretName: analytics-proxy-tls +``` + +Apply the Ingress: + +```bash +kubectl apply -f proxy-ingress.yaml +``` + +Users can now access the proxy at `https://analytics-mcp.company.com`. + +## Check remote proxy status + +To check the status of your remote proxies: + +```bash +# List all proxies +kubectl get mcpremoteproxy -n toolhive-system + +# Get detailed information +kubectl describe mcpremoteproxy analytics-proxy -n toolhive-system + +# Check proxy logs +kubectl logs -n toolhive-system -l app.kubernetes.io/instance=analytics-proxy +``` + +The status shows: + +- **Phase**: Current state (Pending, Ready, Failed, Terminating) +- **URL**: Internal cluster URL +- **External URL**: External URL if exposed via Ingress +- **Conditions**: Detailed status conditions + +## Telemetry and observability + +### Audit logging + +When `audit.enabled: true`, the proxy logs all requests with: + +- User identity (from token claims) +- Request method and parameters +- Response status and duration +- Timestamp + +Audit logs are structured JSON written to stdout: + +```json +{ + "loggedAt": "2025-10-29T10:15:30.123Z", + "type": "mcp_tools_call", + "outcome": "success", + "source": "remote-proxy", + "component": "analytics-proxy", + "subjects": { + "user": "jane.doe@company.com", + "user_id": "user-123" + }, + "target": { + "method": "tools/call", + "name": "query_data" + }, + "metadata": { + "auditId": "550e8400-e29b-41d4-a716-446655440000", + "extra": { + "duration_ms": 234, + "transport": "http" + } + } +} +``` + +### Prometheus metrics + +Enable Prometheus metrics to monitor proxy health and usage: + +```yaml {6-8} +spec: + remoteURL: https://mcp.example.com + # ... other config ... + + telemetry: + prometheus: + enabled: true +``` + +Metrics are exposed at `/metrics` and include: + +- `toolhive_mcp_requests_total` - Total MCP requests +- `toolhive_mcp_request_duration_seconds` - Request duration +- `toolhive_mcp_tool_calls_total` - Tool call operations +- `toolhive_mcp_active_connections` - Number of active connections + +### OpenTelemetry + +For distributed tracing and metrics export: + +```yaml {6-15} +spec: + remoteURL: https://mcp.example.com + # ... other config ... + + telemetry: + openTelemetry: + enabled: true + endpoint: https://otel-collector.company.com:4317 + serviceName: analytics-mcp-proxy + tracing: + enabled: true + samplingRate: '0.1' + metrics: + enabled: true +``` + +See [Telemetry and metrics](./telemetry-and-metrics.mdx) for more information. + +## Use with Virtual MCP Server + +MCPRemoteProxy resources can be added to an MCPGroup and discovered by a +VirtualMCPServer, enabling you to combine remote MCP servers with local +container-based MCPServer resources into a single unified endpoint. + +:::caution[Current limitation] + +vMCP can discover MCPRemoteProxy backends in a group, but authentication between +vMCP and MCPRemoteProxy is not yet fully implemented. Since MCPRemoteProxy +requires `oidcConfig` to validate incoming requests, and vMCP does not currently +forward authentication tokens to backends, vMCP cannot communicate with +MCPRemoteProxy backends that require authentication. + +This limitation will be addressed in a future release. For now, MCPRemoteProxy +works best when accessed directly by clients rather than through vMCP +aggregation. + +::: + +### Add remote proxy to a group + +To include a remote proxy in an MCPGroup for future vMCP aggregation, add the +`groupRef` field to your MCPRemoteProxy spec: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRemoteProxy +metadata: + name: context7-proxy + namespace: toolhive-system +spec: + groupRef: my-group # Reference to an MCPGroup + remoteURL: https://mcp.context7.com/mcp + transport: streamable-http + proxyPort: 8080 + oidcConfig: + type: inline + inline: + issuer: https://auth.company.com + audience: context7-proxy +``` + +### Planned benefits of vMCP aggregation + +When vMCP authentication to MCPRemoteProxy is implemented, the following +benefits will be available: + +- **Unified endpoint**: Clients connect to one vMCP URL instead of multiple + proxy endpoints +- **Centralized authentication**: vMCP handles client authentication at the + entry point +- **Tool namespacing**: Tools from remote proxies are automatically prefixed to + avoid conflicts with local MCP servers +- **Unified toolset**: Combine tools from container-based servers and external + SaaS MCP services + +See [Configure vMCP servers](../guides-vmcp/configuration.mdx) for more vMCP +configuration options. + +## Next steps + +See the [Client compatibility](../reference/client-compatibility.mdx) reference +to learn how to connect to remote MCP proxies using different clients. + +Learn how to customize MCP tools using +[filters and overrides](./customize-tools.mdx). + +Discover your deployed MCP servers automatically using the +[Kubernetes registry](../guides-registry/configuration.mdx#kubernetes-registry) +feature in the ToolHive Registry Server. + +## Related information + +- [Kubernetes CRD reference](../reference/crd-spec.md#apiv1alpha1mcpremoteproxy) - + Full MCPRemoteProxy specification +- [Deploy the operator](./deploy-operator.mdx) - Install the ToolHive operator +- [Run MCP servers in Kubernetes](./run-mcp-k8s.mdx) - Deploy local MCP servers + +## Troubleshooting + +
+Proxy pod in CrashLoopBackOff + +If the proxy pod is restarting continuously: + +```bash +# Check pod status +kubectl get pods -n toolhive-system -l app.kubernetes.io/instance=analytics-proxy + +# Check pod logs +kubectl logs -n toolhive-system -l app.kubernetes.io/instance=analytics-proxy +``` + +Common causes: + +- **Invalid OIDC configuration**: Verify the issuer URL is accessible and + returns valid OIDC discovery metadata +- **Certificate validation issues**: If using a custom CA, ensure `caBundleRef` + is set correctly +- **Resource limits**: Check if the pod has sufficient CPU and memory + +
+ +
+401 Unauthorized errors + +If clients receive 401 errors when calling the proxy: + +```bash +# Check proxy logs for authentication errors +kubectl logs -n toolhive-system -l app.kubernetes.io/instance=analytics-proxy | grep -i "auth" +``` + +Common causes: + +- **Missing audience claim**: Ensure tokens include the expected `aud` claim +- **Token expired**: Check token expiration time +- **Issuer mismatch**: Verify token `iss` claim matches `oidcConfig.issuer` +- **JWKS fetch failure**: Check proxy can reach the OIDC provider's JWKS + endpoint + +Verify your token claims: + +```bash +# Decode JWT payload (requires jq) +# This handles base64url and missing padding +echo "" | cut -d. -f2 | tr '_-' '/+' | awk '{l=length($0)%4; if(l>0) printf "%s", substr("====",1,4-l); print $0}' | base64 -d 2>/dev/null | jq +``` + +:::note + +JWTs use base64url encoding and may omit padding characters (`=`). The command +above converts base64url to standard base64 and adds padding if needed. If you +get an "invalid input" error, check your shell and base64 version, or manually +add padding to the payload. + +::: + +
+ +
+Remote server unreachable + +If the proxy cannot connect to the remote MCP server: + +```bash +# Check proxy logs +kubectl logs -n toolhive-system -l app.kubernetes.io/instance=analytics-proxy + +# Test connectivity from the pod +kubectl exec -n toolhive-system -- \ + curl -v https://mcp.analytics.example.com +``` + +Common causes: + +- **Network policies**: Check if network policies allow egress to the remote + server +- **DNS resolution**: Verify the remote URL resolves correctly +- **Firewall rules**: Ensure the cluster can reach the remote server +- **Certificate issues**: If using HTTPS, verify certificates are valid + +
+ +
+Tool filtering not working + +If tool filtering isn't being applied: + +```bash +# Check if MCPToolConfig exists +kubectl get mcptoolconfig -n toolhive-system + +# Verify the reference in MCPRemoteProxy +kubectl get mcpremoteproxy analytics-proxy -n toolhive-system -o yaml | \ + grep -A2 toolConfigRef +``` + +Ensure: + +- The MCPToolConfig exists in the same namespace as the MCPRemoteProxy +- The `toolConfigRef.name` matches the MCPToolConfig name +- The MCPRemoteProxy status shows the config is applied + +
+ +
+Token exchange failing + +If token exchange is configured but not working: + +```bash +# Check for token exchange errors in logs +kubectl logs -n toolhive-system -l app.kubernetes.io/instance=analytics-proxy | \ + grep -i "token.*exchange" +``` + +Verify: + +- The MCPExternalAuthConfig exists and is correctly configured +- The client secret is valid and stored in a Kubernetes Secret +- The token exchange endpoint is reachable from the proxy +- The company IDP is configured for token exchange (RFC 8693) + +
+ +
+Getting more debug information + +For additional debugging: + +```bash +# Get all resources related to your proxy +kubectl get all -n toolhive-system -l app.kubernetes.io/instance=analytics-proxy + +# Export MCPRemoteProxy for inspection +kubectl get mcpremoteproxy analytics-proxy -n toolhive-system -o yaml + +# Check operator logs +kubectl logs -n toolhive-system -l app.kubernetes.io/name=toolhive-operator + +# Check events +kubectl get events -n toolhive-system --sort-by='.lastTimestamp' | \ + grep analytics-proxy +``` + +
diff --git a/versioned_docs/version-1.1/toolhive/guides-k8s/run-mcp-k8s.mdx b/versioned_docs/version-1.1/toolhive/guides-k8s/run-mcp-k8s.mdx new file mode 100644 index 00000000..2d04e672 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-k8s/run-mcp-k8s.mdx @@ -0,0 +1,682 @@ +--- +title: Run MCP servers in Kubernetes +description: How to deploy MCP servers in Kubernetes using the ToolHive operator +--- + +## Prerequisites + +- A Kubernetes cluster (current and two previous minor versions are supported) +- Permissions to create resources in the cluster +- [`kubectl`](https://kubernetes.io/docs/tasks/tools/) configured to communicate + with your cluster +- The ToolHive operator installed in your cluster (see + [Deploy the operator](./deploy-operator.mdx)) + +## Overview + +The ToolHive operator deploys MCP servers in Kubernetes by creating proxy pods +that manage the actual MCP server containers. + +:::tip + +If you need to build a container image for an MCP server that doesn't already +have one available, see the +[Build MCP containers](../guides-cli/build-containers.mdx) guide to learn how to +quickly create container images using the ToolHive CLI. + +::: + +### High-level architecture + +This diagram shows the basic relationship between components. The ToolHive +operator watches for `MCPServer` resources and automatically creates the +necessary infrastructure to run your MCP servers securely within the cluster. + +```mermaid +flowchart LR + Client["Client"] -->|connects| Proxy["ToolHive
Proxy/Runner"] + Proxy --> MCP["MCP Server"] + + subgraph K8s["Kubernetes Cluster"] + subgraph NS["toolhive-system"] + Operator["ToolHive Operator"] + end + subgraph NS2["MCP Server
Namespace"] + Proxy + MCP + end + end + + Operator -.->|creates| Proxy + Proxy -.->|creates| MCP +``` + +### STDIO transport flow + +For MCP servers using STDIO transport, the proxy directly attaches to the MCP +server pod's standard input/output streams. + +```mermaid +flowchart LR + subgraph Proxy["Created by Operator"] + direction TB + ProxyService["SVC: ToolHive-Proxy"] + ProxyPod["POD: ToolHive-Proxy"] + ProxyService --> ProxyPod + end + + subgraph MCP["Created by Proxy"] + direction TB + MCPPod["POD: MCP Server"] + end + + Client["Client"] -->|HTTP/SSE| Proxy + Proxy -->|Attaches/STDIO| MCP +``` + +:::info[STDIO Transport Limitation] + +MCP servers using STDIO transport support only a single client connection at a +time. If you need to support multiple users or concurrent client connections, +use SSE (Server-Sent Events) or Streamable HTTP transport instead. + +::: + +### Streamable HTTP and SSE transport flow + +For MCP servers using Server-Sent Events (SSE) or Streamable HTTP transport, the +proxy creates both a pod and a headless service. This allows direct HTTP/SSE or +HTTP/Streamable HTTP communication between the proxy and MCP server while +maintaining network isolation and service discovery. + +```mermaid +flowchart LR + subgraph Proxy["Created by Operator"] + direction TB + ProxyService["SVC: ToolHive-Proxy"] + ProxyPod["POD: ToolHive-Proxy"] + ProxyService --> ProxyPod + end + + subgraph MCP["Created by Proxy"] + direction TB + MCPService["SVC: MCP-Headless"] + MCPPod["POD: MCP Server"] + end + + Client["Client"] -->|HTTP| Proxy + Proxy -->|HTTP| MCP + MCPService --> MCPPod +``` + +## Create an MCP server + +You can create `MCPServer` resources in namespaces based on how the operator was +deployed. + +- **Cluster mode (default)**: Create MCPServer resources in any namespace +- **Namespace mode**: Create MCPServer resources only in allowed namespaces + +See [Deploy the operator](./deploy-operator.mdx#operator-deployment-modes) to +learn about the different deployment modes. + +To create an MCP server, define an `MCPServer` resource and apply it to your +cluster. This minimal example creates the +[`osv` MCP server](https://github.com/StacklokLabs/osv-mcp) which queries the +[Open Source Vulnerability (OSV) database](https://osv.dev/) for vulnerability +information. + +```yaml title="my-mcpserver.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: osv + namespace: my-namespace # Update with your namespace +spec: + image: ghcr.io/stackloklabs/osv-mcp/server + transport: streamable-http + mcpPort: 8080 + proxyPort: 8080 + resources: + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' +``` + +Apply the resource: + +```bash +kubectl apply -f my-mcpserver.yaml +``` + +:::info[What's happening?] + +When you apply an `MCPServer` resource, here's what happens: + +1. The ToolHive operator detects the new resource (if it's in an allowed + namespace) +2. The operator automatically creates the necessary RBAC resources in the target + namespace: + - A ServiceAccount with the same name as the MCPServer + - A Role with minimal permissions for StatefulSets, Services, Pods, and Pod + logs/attach operations + - A RoleBinding that connects the ServiceAccount to the Role +3. The operator creates a new Deployment containing a ToolHive proxy pod and + service to handle client connections +4. The proxy creates the actual `MCPServer` pod containing your specified + container image +5. For STDIO transport, the proxy attaches directly to the pod; for SSE and + Streamable HTTP transport, a headless service is created for direct pod + communication +6. Clients can now connect through the service → proxy → MCP server chain to use + the tools and resources (note: external clients will need an ingress + controller or similar mechanism to access the service from outside the + cluster) + +::: + +For more examples of `MCPServer` resources, see the +[example MCP server manifests](https://github.com/stacklok/toolhive/tree/main/examples/operator/mcp-servers) +in the ToolHive repo. + +## Automatic RBAC management + +The ToolHive operator automatically handles RBAC (Role-Based Access Control) for +each MCPServer instance, providing better security isolation and multi-tenant +support. Here's what the operator creates automatically: + +- **ServiceAccount**: A dedicated ServiceAccount with the same name as your + MCPServer +- **Role**: A namespace-scoped Role with minimal permissions for: + - StatefulSets (create, get, list, watch, update, patch, delete) + - Services (create, get, list, watch, update, patch, delete) + - Pods (get, list, watch) + - Pod logs and attach operations (get, list) +- **RoleBinding**: Connects the ServiceAccount to the Role + +This approach provides: + +- Each MCPServer operates with its own minimal set of permissions +- No manual RBAC setup required +- Better security isolation between different MCPServer instances +- Support for multi-tenant deployments across different namespaces + +## Customize server settings + +You can customize the MCP server by adding additional fields to the `MCPServer` +resource. The full specification is available in the +[Kubernetes CRD reference](../reference/crd-spec.md#apiv1alpha1mcpserver). + +Below are some common configurations. + +### Customize the MCP server pod + +You can customize the MCP server pod that gets created by the proxy using the +`podTemplateSpec` field. This gives you full control over the pod specification, +letting you set security contexts, resource limits, node selectors, and other +pod-level configurations. + +The `podTemplateSpec` field follows the standard Kubernetes +[`PodTemplateSpec`](https://kubernetes.io/docs/reference/kubernetes-api/workload-resources/pod-template-v1/#PodTemplateSpec) +format, so you can use any valid pod specification options. + +This example sets resource limits. + +```yaml {14-15} title="my-mcpserver-custom-pod.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: fetch + namespace: development # Can be any namespace +spec: + image: ghcr.io/stackloklabs/gofetch/server + transport: streamable-http + mcpPort: 8080 + proxyPort: 8080 + podTemplateSpec: + spec: + containers: + - name: mcp # This name must be "mcp" + resources: # These resources apply to the MCP container + limits: + cpu: '500m' + memory: '512Mi' + requests: + cpu: '100m' + memory: '128Mi' + resources: # These resources apply to the proxy container + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' +``` + +:::info[Container name requirement] + +When customizing containers in `podTemplateSpec`, you must use `name: mcp` for +the main container. This ensures the proxy can properly manage the MCP server +process. + +::: + +### Run a server with secrets + +When your MCP servers require authentication tokens or other secrets, ToolHive +supports multiple secrets management methods to fit your existing +infrastructure. Choose the method that best suits your needs: + + + + +ToolHive can reference existing Kubernetes secrets to inject sensitive data into +your MCP server pods as environment variables. This example demonstrates how to +pass a GitHub personal access token to the `github` MCP server. + +First, create the secret. The secret must exist in the same namespace as your +MCP server and the key must match what you specify in the `MCPServer` resource. + +```bash +kubectl -n production create secret generic github-token --from-literal=token= +``` + +Next, define the `MCPServer` resource to reference the secret: + +```yaml {10-13} title="my-mcpserver-with-secrets.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: github + namespace: production # Can be any namespace +spec: + image: ghcr.io/github/github-mcp-server + transport: stdio + proxyPort: 8080 + secrets: + - name: github-token + key: token + targetEnvName: GITHUB_PERSONAL_ACCESS_TOKEN +``` + +Finally, apply the MCPServer resource: + +```bash +kubectl apply -f my-mcpserver-with-secrets.yaml +``` + + + + +[External Secrets Operator](https://external-secrets.io/) is a Kubernetes +operator that integrates external secret management systems and syncs secrets +into Kubernetes as native resources. This example demonstrates how to use +ESO-managed secrets with your MCP server. + +:::note + +When you use the External Secrets Operator, your MCP server definition will look +the same as the Kubernetes-native example. This is because the External Secrets +Operator creates standard Kubernetes secrets from external sources. + +::: + +First, create a secret using the +[ExternalSecret resource](https://external-secrets.io/latest/api/externalsecret/). +The exact configuration depends on your external secret management system. The +secret must exist in the same namespace as your MCP server and the key must +match what you specify in the `MCPServer` resource. + +Next, define the `MCPServer` resource to reference the secret: + +```yaml {10-13} title="my-mcpserver-with-secrets-eso.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: github + namespace: production # Can be any namespace +spec: + image: ghcr.io/github/github-mcp-server + transport: stdio + proxyPort: 8080 + secrets: + - name: github-token + key: token + targetEnvName: GITHUB_PERSONAL_ACCESS_TOKEN +``` + +Finally, apply the MCPServer resource: + +```bash +kubectl apply -f my-mcpserver-with-secrets-eso.yaml +``` + + + + +HashiCorp Vault provides multiple integration methods for Kubernetes +environments: + +1. [Vault Sidecar Agent Injector](https://developer.hashicorp.com/vault/docs/deploy/kubernetes/injector), + which injects a sidecar container into your pod to fetch and renew secrets +2. [Vault Secrets Operator](https://developer.hashicorp.com/vault/docs/deploy/kubernetes/vso), + which creates Kubernetes secrets from Vault secrets (similar to the External + Secrets Operator) +3. [Vault CSI Provider](https://developer.hashicorp.com/vault/docs/deploy/kubernetes/csi), + which mounts secrets directly into your pod as files + +ToolHive supports the first two methods. When you use the Vault Secrets +Operator, your MCP server definition will look the same as the Kubernetes-native +example because the Vault Secrets Operator creates standard Kubernetes secrets +from Vault. + +The Vault Sidecar Agent Injector requires additional configuration in your +`MCPServer` resource to add the required annotations. For a complete example, +see the [HashiCorp Vault integration tutorial](../integrations/vault.mdx). + + + + +### Mount a volume + +You can mount volumes into the MCP server pod to provide persistent storage or +access to data. This is useful for MCP servers that need to read/write files or +access large datasets. + +To do this, add a standard `volumes` field to the `podTemplateSpec` in the +`MCPServer` resource and a `volumeMounts` section in the container +specification. Here's an example that mounts a persistent volume claim (PVC) to +the `/projects` path in the Filesystem MCP server. The PVC must already exist in +the same namespace as the MCPServer. + +```yaml {12-15,19-22} title="my-mcpserver-with-volume.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: filesystem + namespace: data-processing # Can be any namespace +spec: + image: docker.io/mcp/filesystem + transport: stdio + proxyPort: 8080 + podTemplateSpec: + spec: + volumes: + - name: my-mcp-data + persistentVolumeClaim: + claimName: my-mcp-data-claim + containers: + - name: mcp + # ... other container settings ... + volumeMounts: + - mountPath: /projects/my-mcp-data + name: my-mcp-data + readOnly: true +``` + +## Check MCP server status + +To check the status of your MCP servers in a specific namespace: + +```bash +kubectl -n get mcpservers +``` + +To check MCP servers across all namespaces: + +```bash +kubectl get mcpservers --all-namespaces +``` + +The status, URL, and age of each MCP server is displayed. + +For more details about a specific MCP server: + +```bash +kubectl -n describe mcpserver +``` + +## Next steps + +- [Connect clients to your MCP servers](./connect-clients.mdx) from outside the + cluster using Ingress or Gateway API, or from within the cluster +- [Secure your servers](./auth-k8s.mdx) with OIDC authentication and Cedar + policies +- [Customize MCP tools](./customize-tools.mdx) with filters and overrides +- [Aggregate multiple servers](../guides-vmcp/index.mdx) into a single endpoint + with Virtual MCP Server +- [Curate a server catalog](../guides-registry/index.mdx) for your team with the + Registry Server + +## Related information + +- [Kubernetes CRD reference](../reference/crd-spec.md#apiv1alpha1mcpserver) - + Reference for the `MCPServer` Custom Resource Definition (CRD) +- [Deploy the operator](./deploy-operator.mdx) - Install the ToolHive operator +- [Build MCP containers](../guides-cli/build-containers.mdx) - Create custom MCP + server container images + +## Troubleshooting + +
+MCPServer resource not creating pods + +If your `MCPServer` resource is created but no pods appear, first ensure you +created the `MCPServer` resource in an allowed namespace. If the operator runs +in namespace mode and you didn't include the namespace in the +`allowedNamespaces` list, the operator ignores the resource. Check the +operator's configuration: + +```bash +helm get values toolhive-operator -n toolhive-system +``` + +Check the `operator.rbac.scope` and `operator.rbac.allowedNamespaces` +properties. If the operator runs in `namespace` mode, add the namespace where +you created the `MCPServer` to the `allowedNamespaces` list. See +[Operator deployment modes](./deploy-operator.mdx#operator-deployment-modes). + +If the operator runs in `cluster` mode (default) or the `MCPServer` is in an +allowed namespace, check the operator logs and resource status: + +```bash +# Check MCPServer status +kubectl -n describe mcpserver + +# Check operator logs +kubectl -n toolhive-system logs -l app.kubernetes.io/name=toolhive-operator + +# Verify the operator is running +kubectl -n toolhive-system get pods -l app.kubernetes.io/name=toolhive-operator +``` + +Other common causes include: + +- **Operator not running**: Ensure the ToolHive operator is deployed and running +- **Invalid image reference**: Verify the container image exists and is + accessible +- **RBAC issues**: The operator automatically creates RBAC resources, but check + for cluster-level permission issues +- **Resource quotas**: Check if namespace resource quotas prevent pod creation + +
+ +
+MCP server pod fails to start + +If the MCP server pod is created but fails to start or is in `CrashLoopBackOff`: + +```bash +# Check pod status +kubectl -n get pods + +# Describe the failing pod +kubectl -n describe pod + +# Check pod logs +kubectl -n logs -c mcp +``` + +Common causes include: + +- **Image pull errors**: Verify the container image is accessible and the image + name is correct +- **Missing secrets**: Ensure required secrets exist and are properly referenced +- **Resource constraints**: Check if the pod has sufficient CPU and memory + resources +- **Permission issues**: Verify the security context and RBAC permissions are + correctly configured +- **Invalid arguments**: Check if the `args` field contains valid arguments for + the MCP server + +
+ +
+Proxy pod connection issues + +If the proxy pod is running but clients cannot connect: + +```bash +# Check proxy pod status +kubectl -n get pods -l app.kubernetes.io/instance= + +# Check proxy logs +kubectl -n logs -l app.kubernetes.io/instance= + +# Verify service is created +kubectl -n get services +``` + +Common causes include: + +- **Service not created**: Ensure the proxy service exists and has the correct + selectors +- **Port configuration**: Verify the `port` field matches the MCP server's + listening port +- **Transport mismatch**: Ensure the `transport` field + (stdio/sse/streamable-http) matches the MCP server's capabilities +- **Network policies**: Check if network policies are blocking communication + +
+ +
+Secret mounting issues + +If secrets are not being properly mounted or environment variables are missing: + +```bash +# Check if secret exists +kubectl -n get secret + +# Verify secret content +kubectl -n describe secret + +# Check environment variables in the pod +kubectl -n exec -c mcp -- env | grep +``` + +Common causes include: + +- **Secret doesn't exist**: Create the secret in the correct namespace +- **Wrong key name**: Ensure the `key` field matches the actual key in the + secret +- **Namespace mismatch**: Secrets must be in the same namespace as the + `MCPServer` +- **Permission issues**: The operator automatically creates the necessary RBAC + resources, but verify the ServiceAccount has access to read secrets + +
+ +
+Volume mounting problems + +If persistent volumes or other volumes are not mounting correctly: + +```bash +# Check PVC status +kubectl -n get pvc + +# Describe the PVC +kubectl -n describe pvc + +# Check volume mounts in the pod +kubectl -n describe pod +``` + +Common causes include: + +- **PVC not bound**: Ensure the PersistentVolumeClaim is bound to a + PersistentVolume +- **Namespace mismatch**: The PVC must be in the same namespace as the MCPServer +- **Storage class issues**: Verify the storage class exists and is available +- **Access mode conflicts**: Check that the access mode is compatible with your + setup +- **Mount path conflicts**: Ensure mount paths don't conflict with existing + directories + +
+ +
+Resource limit issues + +If pods are being killed due to resource constraints: + +```bash +# Check resource usage +kubectl -n top pods + +# Check for resource limit events +kubectl -n get events --sort-by='.lastTimestamp' + +# Describe the pod for resource information +kubectl -n describe pod +``` + +Solutions: + +- **Increase resource limits**: Adjust `resources.limits` in the `MCPServer` + spec +- **Optimize resource requests**: Set appropriate `resources.requests` values +- **Check node capacity**: Ensure cluster nodes have sufficient resources +- **Review resource quotas**: Check namespace resource quotas and limits + +
+ +
+Debugging connectivity + +To test connectivity between components: + +```bash +# Port-forward to test direct access to the proxy +kubectl -n port-forward service/ 8080:8080 + +# Test the connection locally +curl http://localhost:8080/health + +# Check service endpoints +kubectl -n get endpoints +``` + +
+ +
+Getting more debug information + +For additional debugging information: + +```bash +# Get all resources related to your MCP server +kubectl -n get all -l app.kubernetes.io/instance= + +# Check operator events +kubectl -n get events --field-selector involvedObject.kind=MCPServer + +# Export MCPServer resource for inspection +kubectl -n get mcpserver -o yaml +``` + +
diff --git a/versioned_docs/version-1.1/toolhive/guides-k8s/telemetry-and-metrics.mdx b/versioned_docs/version-1.1/toolhive/guides-k8s/telemetry-and-metrics.mdx new file mode 100644 index 00000000..add0e61e --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-k8s/telemetry-and-metrics.mdx @@ -0,0 +1,390 @@ +--- +title: Telemetry and metrics +description: How to enable OpenTelemetry (metrics and traces) and Prometheus + instrumentation for ToolHive MCP servers inside of Kubernetes using the + ToolHive Operator +--- + +ToolHive includes built-in instrumentation using OpenTelemetry, which gives you +comprehensive observability for your MCP server interactions. You can export +traces and metrics to popular observability backends like Jaeger, Honeycomb, +Datadog, and Grafana Cloud, or expose Prometheus metrics directly. + +## What you can monitor + +ToolHive's telemetry captures detailed information about MCP interactions +including traces, metrics, and performance data. For a comprehensive overview of +the telemetry architecture, metrics collection, and monitoring capabilities, see +the [observability overview](../concepts/observability.mdx). + +## Enable telemetry + +There are two ways to configure telemetry: a shared `MCPTelemetryConfig` +resource (recommended) or inline `spec.telemetry` on each MCPServer. + +### Shared telemetry configuration (recommended) + +The `MCPTelemetryConfig` CRD lets you define telemetry settings once and +reference them from multiple MCPServer resources. Each server can override its +`serviceName` for distinct identity in your observability backend. + +**Step 1: Create an MCPTelemetryConfig resource** + +```yaml title="shared-otel-config.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPTelemetryConfig +metadata: + name: shared-otel + namespace: toolhive-system +spec: + openTelemetry: + enabled: true + endpoint: otel-collector-opentelemetry-collector.monitoring.svc.cluster.local:4318 + insecure: true + metrics: + enabled: true + tracing: + enabled: true + samplingRate: '0.05' + sensitiveHeaders: + - name: Authorization + secretKeyRef: + name: otel-auth-secret + key: api-key + prometheus: + enabled: true +``` + +```bash +kubectl apply -f shared-otel-config.yaml +``` + +**Step 2: Reference from an MCPServer** + +```yaml {10-12} title="mcpserver-with-shared-otel.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: gofetch + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/gofetch/server + transport: streamable-http + proxyPort: 8080 + telemetryConfigRef: + name: shared-otel + serviceName: mcp-fetch-server +``` + +```bash +kubectl apply -f mcpserver-with-shared-otel.yaml +``` + +**Step 3: Verify** + +```bash +kubectl get mcpotel -n toolhive-system +``` + +The `REFERENCES` column shows which workloads use this config. The `READY` +column confirms validation passed. + +:::tip[Sensitive headers] + +Use `sensitiveHeaders` to pass API keys or tokens to your OTLP endpoint via +Kubernetes Secrets, instead of storing credentials in plain text. A header name +cannot appear in both `headers` and `sensitiveHeaders`. + +::: + +### Inline telemetry configuration + +:::warning[Deprecated] + +The inline `spec.telemetry` field on MCPServer is deprecated and will be removed +in a future release. Use `telemetryConfigRef` to reference a shared +MCPTelemetryConfig resource instead. You cannot set both fields on the same +MCPServer. + +::: + +You can enable telemetry inline when deploying an MCP server by specifying +telemetry configuration in the MCPServer or MCPRemoteProxy custom resource. + +This example runs the Fetch MCP server and exports traces to a deployed instance +of the [OpenTelemetry Collector](https://opentelemetry.io/docs/collector/): + +```yaml {12} +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer # or MCPRemoteProxy +metadata: + name: gofetch + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/gofetch/server + transport: streamable-http + proxyPort: 8080 + mcpPort: 8080 + # ... other spec fields ... + telemetry: + openTelemetry: + enabled: true + endpoint: otel-collector-opentelemetry-collector.monitoring.svc.cluster.local:4318 + serviceName: mcp-fetch-server + insecure: true + metrics: + enabled: true + tracing: + enabled: true + samplingRate: '0.05' + prometheus: + enabled: true +``` + +The `spec.telemetry.openTelemetry.endpoint` will be the OpenTelemetry collector +that is deployed inside of your infrastructure, the +`spec.telemetry.openTelemetry.serviceName` will be what you can use to identify +your MCP server in your observability stack. + +### Export metrics to an OTLP endpoint + +If you want to enable ToolHive to export metrics to your OTel collector, you can +enable the `spec.telemetry.openTelemetry.metrics.enabled` flag. + +### Export traces to an OTLP endpoint + +If you want to enable ToolHive to export tracing information, you can enable the +`spec.telemetry.openTelemetry.tracing.enabled` flag. + +You can also set the sampling rate of your traces by setting the +`spec.telemetry.openTelemetry.tracing.sampleRate` option to a number between 0 +and 1.0. By default this will be `0.05` which equates to 5% of all requests. + +:::note + +The `spec.telemetry.openTelemetry.endpoint` is provided as a hostname and +optional port, without a scheme or path (e.g., use `api.honeycomb.io` or +`api.honeycomb.io:443`, not `https://api.honeycomb.io`). ToolHive automatically +uses HTTPS unless `--otel-insecure` is specified. + +::: + +By default, the service name is set to `toolhive-mcp-proxy`, and the sampling +rate is `0.05` (5%). + +:::tip[Recommendation] + +Set the `spec.telemetry.openTelemetry.serviceName` flag to a meaningful name for +each MCP server. This helps you identify the server in your observability +backend. + +::: + +### Enable Prometheus metrics + +You can expose Prometheus-style metrics at `/metrics` on the main transport port +for local scraping by enabling the `spec.telemetry.prometheus.enabled` flag. + +To access the metrics, you can use `curl` or any Prometheus-compatible scraper. +The metrics are available at `http://:/metrics`, where `` is +resolvable address of the ToolHive ProxyRunner fronting your MCP server pod and +`` is the port of which the ProxyRunner service is configured to expose +for traffic. + +### Dual export + +You can export to both an OTLP endpoint and expose Prometheus metrics +simultaneously. + +The shared `MCPTelemetryConfig` example and the inline `MCPServer` example above +both have dual export enabled. + +## Observability backends + +ToolHive can export telemetry data to many different observability backends. It +supports exporting traces and metrics to any backend that implements the OTLP +protocol. Some common examples are listed below, but specific configurations +will vary based on your environment and requirements. + +:::note + +The backend examples below show inline `spec.telemetry` configuration. The same +endpoint and settings apply when using a shared `MCPTelemetryConfig` resource +via `telemetryConfigRef`. + +::: + +### OpenTelemetry Collector (recommended) + +The OpenTelemetry Collector is a vendor-agnostic way to receive, process and +export telemetry data. It supports many backend services, scalable deployment +options, and advanced processing capabilities. + +```mermaid +graph LR + A[ToolHive] -->|traces & metrics| B[OpenTelemetry Collector] + B --> C[AWS CloudWatch] + B --> D[Splunk] + B --> E[New Relic] + B <--> F[Prometheus] + B --> G[Other OTLP backends] +``` + +You can run the OpenTelemetry Collector inside of a Kubernetes cluster, follow +the +[OpenTelemetry Collector documentation](https://opentelemetry.io/docs/collector/) +for more information. + +To export data to a local OpenTelemetry Collector, set your OTLP endpoint to the +OTLP http receiver port (default is `4318`): + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: gofetch + namespace: toolhive-system +spec: + ... + ... + telemetry: + openTelemetry: + enabled: true + endpoint: otel-collector-opentelemetry-collector.monitoring.svc.cluster.local:4318 + serviceName: mcp-fetch-server + insecure: true + metrics: + enabled: true +``` + +### Prometheus + +To collect metrics using Prometheus, run your MCP server with the +`spec.telemetry.prometheus.enabled` flag enabled and add the following to your +Prometheus configuration: + +```yaml title="prometheus.yml" +scrape_configs: + - job_name: 'toolhive-mcp-proxy' + static_configs: + - targets: [':'] + scrape_interval: 15s + metrics_path: /metrics +``` + +You can add multiple MCP servers to the `targets` list. Replace +`` with the ProxyRunner SVC name and +`` with the port number exposed by the SVC. + +### Jaeger + +[Jaeger](https://www.jaegertracing.io) is a popular open-source distributed +tracing system. You can run it inside of a Kubernetes cluster in order to store +tracing telemetry data exported by the ToolHive proxy. + +You can export traces to Jaeger by setting the OTLP endpoint to an OpenTelemetry +collector, and then configuring the collector to export tracing data to Jaeger. + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: gofetch + namespace: toolhive-system +spec: + ... + ... + telemetry: + openTelemetry: + enabled: true + endpoint: otel-collector-opentelemetry-collector.monitoring.svc.cluster.local:4318 + serviceName: mcp-fetch-server + insecure: true + tracing: + enabled: true +``` + +Inside of your OpenTelemetry collector configuration. + +```yaml +config: + receivers: + otlp: + protocols: + grpc: + endpoint: 0.0.0.0:4317 + http: + endpoint: 0.0.0.0:4318 + + exporters: + otlp/jaeger: + endpoint: http://jaeger-all-in-one-collector.monitoring:4317 + + service: + pipelines: + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp/jaeger] +``` + +### Honeycomb + +Coming soon. + +You'll need your Honeycomb API key, which you can find in your +[Honeycomb account settings](https://ui.honeycomb.io/account). + +### Datadog + +Datadog has [multiple options](https://docs.datadoghq.com/opentelemetry/) for +collecting OpenTelemetry data: + +- The + [**OpenTelemetry Collector**](https://docs.datadoghq.com/opentelemetry/setup/collector_exporter/) + is recommended for existing OpenTelemetry users or users wanting a + vendor-neutral solution. + +- The [**Datadog Agent**](https://docs.datadoghq.com/opentelemetry/setup/agent) + is recommended for existing Datadog users. + +### Grafana Cloud + +Coming soon. + +## Performance considerations + +### Sampling rates + +Adjust sampling rates based on your environment: + +- **Development**: `spec.telemetry.openTelemetry.tracing.samplingRate: 1.0` + (100% sampling) +- **Production**: `spec.telemetry.openTelemetry.tracing.samplingRate 0.01` (1% + sampling for high-traffic systems) +- **Default**: `spec.telemetry.openTelemetry.tracing.samplingRate 0.05` (5% + sampling) + +### Network overhead + +Telemetry adds minimal overhead when properly configured: + +- Use appropriate sampling rates for your traffic volume +- Monitor your observability backend costs and adjust sampling accordingly + +## Next steps + +- [Set up audit logging](./logging.mdx) for structured request and authorization + event tracking +- [Secure your servers](./auth-k8s.mdx) with authentication and authorization + +## Related information + +- Tutorial: + [Collect telemetry for MCP workloads](../integrations/opentelemetry.mdx) - + step-by-step guide to set up a local observability stack +- [Telemetry and monitoring concepts](../concepts/observability.mdx) - overview + of ToolHive's observability architecture +- [Kubernetes CRD reference](../reference/crd-spec.md#apiv1alpha1mcpserver) - + Reference for the `MCPServer` Custom Resource Definition (CRD) +- [Deploy the operator](./deploy-operator.mdx) - Install the ToolHive operator diff --git a/versioned_docs/version-1.1/toolhive/guides-k8s/token-exchange-k8s.mdx b/versioned_docs/version-1.1/toolhive/guides-k8s/token-exchange-k8s.mdx new file mode 100644 index 00000000..aa666bc2 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-k8s/token-exchange-k8s.mdx @@ -0,0 +1,273 @@ +--- +title: Configure token exchange for backend authentication +description: + How to set up token exchange so MCP servers can authenticate to backend + services in Kubernetes using the ToolHive Operator. +--- + +This guide shows you how to configure token exchange in Kubernetes, which allows +MCP servers to authenticate to backend APIs using short-lived, properly scoped +tokens instead of embedded secrets. + +For conceptual background on how token exchange works, see +[Backend authentication](../concepts/backend-auth.mdx). For CLI-based setup, see +[Configure token exchange](../guides-cli/token-exchange.mdx). + +## Prerequisites + +Before you begin, make sure you have: + +- Kubernetes cluster with RBAC enabled +- ToolHive Operator installed (see + [Deploy the ToolHive Operator](./deploy-operator.mdx)) +- `kubectl` access to your cluster +- An identity provider that supports + [RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693) token exchange (such + as Okta, Auth0, or Keycloak) +- A backend service configured to accept tokens from your identity provider +- Familiarity with + [Authentication and authorization in Kubernetes](./auth-k8s.mdx) + +## Configure your identity provider + +Token exchange requires your identity provider to issue tokens for the backend +service when presented with a valid MCP server token. This involves: + +- Registering a token exchange client with credentials +- Defining audience and scopes for the backend service +- Creating access policies that permit token exchange + +For detailed IdP configuration steps, see +[Configure your identity provider](../guides-cli/token-exchange.mdx#configure-your-identity-provider) +in the CLI guide. + +## Create the token exchange configuration + +### Step 1: Create a Secret for client credentials + +Store the OAuth client secret that ToolHive uses to authenticate when performing +token exchange: + +```yaml title="token-exchange-secret.yaml" +apiVersion: v1 +kind: Secret +metadata: + name: token-exchange-secret + namespace: toolhive-system +type: Opaque +stringData: + client-secret: '' +``` + +```bash +kubectl apply -f token-exchange-secret.yaml +``` + +### Step 2: Create the MCPExternalAuthConfig resource + +Create an `MCPExternalAuthConfig` resource that defines the token exchange +parameters: + +```yaml title="token-exchange-config.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPExternalAuthConfig +metadata: + name: backend-token-exchange + namespace: toolhive-system +spec: + type: tokenExchange + tokenExchange: + tokenUrl: '' + audience: '' + clientId: '' + clientSecretRef: + name: token-exchange-secret + key: client-secret + scopes: + - '' +``` + +```bash +kubectl apply -f token-exchange-config.yaml +``` + +### Configuration reference + +| Field | Description | +| ----------------- | -------------------------------------------------------------- | +| `tokenUrl` | Your identity provider's token exchange endpoint | +| `audience` | Target audience for the exchanged token (your backend service) | +| `clientId` | Client ID for ToolHive to authenticate to the IdP | +| `clientSecretRef` | Reference to the Secret containing the client secret | +| `scopes` | Scopes to request for the backend service | + +## MCP server requirements + +The MCP server that ToolHive fronts must accept a per-request authentication +token. Specifically, the server should: + +- Read the access token from the `Authorization: Bearer` header +- Use this token to authenticate to the backend service +- Not rely on hardcoded secrets or environment variables for backend + authentication + +ToolHive injects the exchanged token into each request, so the MCP server +receives a fresh, properly scoped token for every call. + +## Deploy an MCP server with token exchange + +Create an `MCPServer` resource that references the token exchange configuration: + +```yaml title="mcpserver-token-exchange.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: my-mcp-server + namespace: toolhive-system +spec: + image: + transport: streamable-http + proxyPort: 8080 + # Reference the token exchange configuration + externalAuthConfigRef: + name: backend-token-exchange + # OIDC configuration for validating incoming client tokens + oidcConfig: + type: inline + inline: + issuer: '' + audience: '' + jwksUrl: '' +``` + +```bash +kubectl apply -f mcpserver-token-exchange.yaml +``` + +The `externalAuthConfigRef` tells ToolHive to use the token exchange +configuration you created earlier. The `oidcConfig` validates incoming client +tokens before performing the exchange. + +## Verify the configuration + +To confirm token exchange is working: + +1. Check the MCPServer status: + + ```bash + kubectl get mcpserver -n toolhive-system my-mcp-server + ``` + +2. Optionally, expose the server outside the cluster using an Ingress or Gateway + +3. Connect to the MCP server with a client that supports authentication + +4. Make a tool call that requires backend access + +5. Check the proxy logs for successful token exchange: + + ```bash + kubectl logs -n toolhive-system -l app.kubernetes.io/name=my-mcp-server + ``` + +You can also verify by examining your identity provider's logs for successful +token exchange requests, or by checking audit logs on your backend service to +confirm requests arrive with the correct user identity and scopes. + +## Example: Okta configuration + +This example shows a complete configuration using Okta for token exchange. + +### Secret + +```yaml title="okta-secret.yaml" +apiVersion: v1 +kind: Secret +metadata: + name: okta-token-exchange-secret + namespace: toolhive-system +type: Opaque +stringData: + client-secret: 'your-okta-client-secret' +``` + +### MCPExternalAuthConfig + +```yaml title="okta-token-exchange.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPExternalAuthConfig +metadata: + name: okta-backend-exchange + namespace: toolhive-system +spec: + type: tokenExchange + tokenExchange: + tokenUrl: 'https://dev-123456.okta.com/oauth2/aus9876543210/v1/token' + audience: 'backend-api' + clientId: '0oa0987654321fedcba' + clientSecretRef: + name: okta-token-exchange-secret + key: client-secret + scopes: + - 'api:read' + - 'api:write' +``` + +### MCPServer + +```yaml title="mcpserver-okta.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: my-backend-server + namespace: toolhive-system +spec: + image: your-mcp-server:latest + transport: streamable-http + proxyPort: 8080 + externalAuthConfigRef: + name: okta-backend-exchange + oidcConfig: + type: inline + # Set resourceUrl to the external URL if exposing outside the cluster + resourceUrl: 'https://my-backend-server.example.com' + inline: + issuer: 'https://dev-123456.okta.com/oauth2/aus1234567890' + audience: 'mcp-server' + jwksUrl: 'https://dev-123456.okta.com/oauth2/aus1234567890/v1/keys' +``` + +Key points in this example: + +- **Two authorization servers**: The `issuer` in `oidcConfig` (`aus1234567890`) + validates incoming client tokens. The `tokenUrl` in `MCPExternalAuthConfig` + uses a different authorization server (`aus9876543210`) that issues tokens for + the backend API. +- **Audience transformation**: Client tokens arrive with audience `mcp-server`. + ToolHive exchanges them for tokens with audience `backend-api`, which the + backend service expects. +- **Scope transformation**: The original token has MCP-specific scopes, while + the exchanged token has backend-specific scopes (`api:read`, `api:write`). The + user's identity is preserved, but the permissions are transformed for the + target service. + +## Next steps + +- [Monitor server activity](./telemetry-and-metrics.mdx) with OpenTelemetry and + Prometheus +- [Set up audit logging](./logging.mdx) to track requests and authentication + decisions + +## Related information + +- [Backend authentication](../concepts/backend-auth.mdx) - conceptual overview + of token exchange and federation +- [Configure token exchange (CLI)](../guides-cli/token-exchange.mdx) - CLI-based + setup +- [Authentication and authorization](./auth-k8s.mdx) - basic auth setup for MCP + servers in Kubernetes +- [CRD specification](../reference/crd-spec.md) - complete CRD reference + including MCPExternalAuthConfig +- [AWS STS integration](../integrations/aws-sts.mdx) - for AWS services, + ToolHive has built-in STS support using `MCPExternalAuthConfig` with + `type: awsSts` diff --git a/versioned_docs/version-1.1/toolhive/guides-mcp/_template.mdx b/versioned_docs/version-1.1/toolhive/guides-mcp/_template.mdx new file mode 100644 index 00000000..cbf8ba8e --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-mcp/_template.mdx @@ -0,0 +1,60 @@ +--- +title: SERVER_NAME MCP server guide +sidebar_label: SERVER_NAME +description: Using the SERVER_NAME MCP server with ToolHive for PURPOSE. +last_update: + author: YOUR_GITHUB_USERNAME + date: YYYY-MM-DD +--- + +## Overview + +A brief overview of the MCP server, its purpose, and key features. Link to the +official documentation. + +## Metadata + + + +## Usage + + + + +UI instructions go here. Only include a screenshot if it adds value, such as +showing a unique configuration option or feature. + + + + +CLI instructions go here, with multiple usage examples specific to the MCP +server. + +```bash +thv run ... +``` + +If appropriate, include guidance for using network isolation and/or a custom +permission profile. + + + + +Kubernetes manifest and instructions go here + +```yaml title="SERVER_NAME.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +``` + + + + +## Sample prompts + +Provide sample prompts that users can use to interact with the MCP server. + +## Recommended practices + +- Include some recommended practices for using the MCP server safely and + effectively diff --git a/versioned_docs/version-1.1/toolhive/guides-mcp/context7.mdx b/versioned_docs/version-1.1/toolhive/guides-mcp/context7.mdx new file mode 100644 index 00000000..b757d880 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-mcp/context7.mdx @@ -0,0 +1,179 @@ +--- +title: Context7 MCP server guide +sidebar_label: Context7 +description: Using the Context7 MCP server with ToolHive for up-to-date documentation. +last_update: + author: danbarr + date: 2026-02-10 +--- + +## Overview + +The [Context7 MCP server](https://github.com/upstash/context7) provides +up-to-date, version-specific documentation and code examples straight from the +source for any library or framework. Instead of relying on outdated training +data, Context7 fetches current documentation and API references directly into +your LLM's context, ensuring you get working code examples and accurate +information. + +Context7 eliminates common issues like hallucinated APIs that don't exist, +outdated code patterns, and generic answers based on old package versions. It +supports thousands of popular libraries including Next.js, React, MongoDB, +Supabase, and many more. + +Learn more at [context7.com](https://context7.com) and view the +[project documentation](https://github.com/upstash/context7) for additional +details. + +## Metadata + + + +## Usage + +While Context7 works without an API key, registering at +[context7.com/dashboard](https://context7.com/dashboard) provides: + +- Higher rate limits +- Priority access during peak usage +- Better performance for frequent queries + + + + +**Remote MCP server** + +If you have a Context7 account, you can select the `context7-remote` MCP server +to connect to the hosted service with dynamic OAuth authentication. Your browser +will open to authorize the server to access your Context7 account. + +**Local MCP server** + +Select the `context7` MCP server in the ToolHive registry. + +The server works without authentication for basic usage, but you can optionally +add an API key in the **Secrets** section for higher rate limits. + +:::tip[Security tip] + +Enable outbound network filtering on the **Network Isolation** tab to restrict +the server's network access using the default profile contained in the registry. + +::: + + + + +If you have a Context7 account, you can run the remote MCP server with dynamic +OAuth authentication. Your browser will open to authorize the server to access +your Context7 account. + +```bash +thv run context7-remote +``` + +Alternatively, run the local containerized MCP server with the default +configuration (you don't need an API key for basic usage): + +```bash +thv run context7 +``` + +Enable [network isolation](../guides-cli/network-isolation.mdx) using the +default profile from the registry to restrict the server's network access: + +```bash +thv run --isolate-network context7 +``` + +If you have a Context7 API key for higher rate limits, create a secret named +`context7` containing your API key and run the server with the `--secret` flag: + +```bash +thv secret set context7 +thv run --secret context7,target=CONTEXT7_API_KEY context7 +``` + +Combine API key with network isolation: + +```bash +thv run --secret context7,target=CONTEXT7_API_KEY --isolate-network context7 +``` + + + + +For basic usage without authentication: + +```yaml title="context7.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: context7 + namespace: toolhive-system +spec: + image: ghcr.io/stacklok/dockyard/npx/context7:2.1.4 + transport: stdio + proxyPort: 8080 +``` + +Apply the manifest to your cluster: + +```bash +kubectl apply -f context7.yaml +``` + +If you have a Context7 API key for higher rate limits, create a Kubernetes +secret containing your key: + +```bash +kubectl -n toolhive-system create secret generic context7-api-key --from-literal=token= +``` + +Then add a `secrets` section to the manifest file above to pass the API key as +an environment variable: + +```yaml title="context7-with-auth.yaml" +spec: + # ... + secrets: + - name: context7-api-key + key: token + targetEnvName: CONTEXT7_API_KEY +``` + + + + +## Sample prompts + +Here are practical prompts you can use with the Context7 MCP server: + +- "Create a Next.js middleware that checks for a valid JWT in cookies and + redirects unauthenticated users to `/login`. Use context7" +- "Show me how to set up MongoDB connection pooling with the latest MongoDB + Node.js driver. Use context7" +- "Configure a Supabase client with TypeScript types for a user authentication + system. Use context7" +- "Create a React component using the latest Tailwind CSS utility classes for a + responsive navigation bar. Use context7" +- "Show me how to implement server-side rendering with the current version of + Nuxt.js. Use context7" +- "Configure Redis caching with the Upstash Redis SDK for serverless functions. + Use context7" + +## Recommended practices + +- Include `use context7` in your prompts to automatically fetch current + documentation for the libraries you're working with. +- When you know the exact library, specify it directly using the Context7 ID + format, for example: `use library /supabase/supabase for api and docs` +- Consider setting up a rule in your MCP client to automatically invoke Context7 + for code-related queries, eliminating the need to manually add `use context7` + to each prompt. +- Use the `topic` parameter when requesting documentation to focus on specific + areas like "routing", "authentication", or "deployment". +- Register for an API key at + [context7.com/dashboard](https://context7.com/dashboard) if you plan to make + frequent requests or need higher rate limits. +- Enable network isolation to restrict the server's outbound access. diff --git a/versioned_docs/version-1.1/toolhive/guides-mcp/fetch.mdx b/versioned_docs/version-1.1/toolhive/guides-mcp/fetch.mdx new file mode 100644 index 00000000..b9dc2226 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-mcp/fetch.mdx @@ -0,0 +1,111 @@ +--- +title: Fetch MCP server guide +sidebar_label: Fetch +description: Using the Fetch MCP server with ToolHive to retrieve website data. +last_update: + author: danbarr + date: 2026-02-10 +--- + +## Overview + +A simple, lightweight MCP server that retrieves data from a website to add +real-time context to an AI agent workflow. + +[GoFetch](https://github.com/StacklokLabs/gofetch) is a Go implementation of the +original +[Fetch MCP server](https://github.com/modelcontextprotocol/servers/tree/main/src/fetch) +with improved performance and security, built by Stacklok. + +## Metadata + + + +## Usage + + + + +Select the `fetch` MCP server in the ToolHive registry. No additional +configuration is required to run it. + +By default, it can access any website. To restrict its network access, +[enable **network isolation**](../guides-ui/network-isolation.mdx) and enter the +allowed hosts and ports. + + + + +Run with the default configuration: + +```bash +thv run fetch +``` + +To control which website resources the server can access, create a custom +permission profile: + +```json title="fetch-profile.json" +{ + "network": { + "outbound": { + "insecure_allow_all": false, + "allow_host": [ + "host.docker.internal", + "intranet.example.com", + ".googleapis.com" + ], + "allow_port": [443] + } + } +} +``` + +Then run the server with the profile and +[enable network isolation](../guides-cli/network-isolation.mdx): + +```bash +thv run --isolate-network --permission-profile fetch-profile.json fetch +``` + + + + +Create a Kubernetes manifest to deploy the Fetch MCP server: + +```yaml title="fetch.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: fetch + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/gofetch/server:1.0.3 + transport: streamable-http + mcpPort: 8080 + proxyPort: 8080 +``` + +Apply the manifest to your Kubernetes cluster: + +```bash +kubectl apply -f fetch.yaml +``` + + + + +## Sample prompts + +Here are some sample prompts you can use to interact with the Fetch MCP server: + +- "Fetch the latest news from `https://news.ycombinator.com`" +- "Get the current weather for `https://weather.com`" +- "Retrieve the latest blog posts from `https://example.com/blog`" + +## Recommended practices + +- Use network isolation to restrict the server's outbound network access to the + specific hosts and ports required for your use case. +- Enable [telemetry](../guides-cli/telemetry-and-metrics.mdx) to monitor tool + usage including URL access for security and auditing purposes. diff --git a/versioned_docs/version-1.1/toolhive/guides-mcp/filesystem.mdx b/versioned_docs/version-1.1/toolhive/guides-mcp/filesystem.mdx new file mode 100644 index 00000000..f739a60d --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-mcp/filesystem.mdx @@ -0,0 +1,175 @@ +--- +title: Filesystem MCP server guide +sidebar_label: Filesystem +description: Using the Filesystem MCP server with ToolHive for file access. +last_update: + author: danbarr + date: 2026-02-10 +--- + +import useBaseUrl from '@docusaurus/useBaseUrl'; +import ThemedImage from '@theme/ThemedImage'; + +## Overview + +The +[Filesystem MCP server](https://github.com/modelcontextprotocol/servers/tree/main/src/filesystem) +provides access to the local filesystem, allowing AI agents to read and write +files as part of their workflows. + +:::note + +Since most AI agent host applications like IDEs already have access to your +working directory, this MCP server is primarily useful for access to files +outside your working directory, headless environments where the host application +does not provide filesystem access, or for demonstrating MCP capabilities. + +::: + +## Metadata + + + +## Usage + + + + +Select the `filesystem` MCP server in the ToolHive registry. + +In the **Storage volumes** section, +[add local files or folders](../guides-ui/run-mcp-servers.mdx#volumes) to expose +to the MCP server. In the drop-down, choose whether to mount the volume as +read-only or read-write. + +:::note + +By default, the server expects files to be located in `/projects`. If you use a +different container path, you must update the command arguments to replace +`/projects` with your custom path. + +::: + + + +:::tip[Security tip] + +Since the server does not require any network access, +[enable **network isolation**](../guides-ui/network-isolation.mdx) and do not +add any hosts or ports to completely restrict its outbound network access. + +::: + + + + +[Mount a directory](../guides-cli/filesystem-access.mdx) from the host +filesystem to the MCP server using the default container path: + +```bash +thv run --volume /path/to/host/directory:/projects filesystem +``` + +:::note + +By default, the server expects files to be located in `/projects`. If you use a +different container path, you must update the command arguments to replace +`/projects` with your custom path. + +::: + +Mount multiple files or directories by repeating the `--volume` flag. This +example mounts a directory under `/projects` and a file under `/data`, and +updates the command arguments accordingly: + +```bash +thv run \ + --volume /path/to/host/directory1:/projects/dir1 \ + --volume /path/to/host/file.txt:/data/file.txt:ro \ + filesystem -- /projects /data +``` + +:::tip + +Since the server does not require any network access, add the +`--isolate-network --permission-profile none` flags to completely restrict its +outbound network access (see the +[network isolation guide](../guides-cli/network-isolation.mdx)). + +::: + + + + +Create a Kubernetes manifest to deploy the Filesystem MCP server with a +[persistent volume](../guides-k8s/run-mcp-k8s.mdx#mount-a-volume). + +Update the `podTemplateSpec` section to include your specific volume claim and +mount path: + +```yaml {14-17,20-23} title="filesystem.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: filesystem + namespace: toolhive-system +spec: + image: mcp/filesystem:1.0.2 + transport: stdio + proxyPort: 8080 + args: + - '/projects' # Update if you use a different mountPath below + podTemplateSpec: + spec: + volumes: + - name: my-mcp-data + persistentVolumeClaim: + claimName: my-mcp-data-claim + containers: + - name: mcp + volumeMounts: + - mountPath: /projects/my-mcp-data + name: my-mcp-data + readOnly: true +``` + +:::note + +If you change the mount path from `/projects`, you must also update the `args` +section to include the path. + +::: + +Apply the manifest to your Kubernetes cluster: + +```bash +kubectl apply -f filesystem.yaml +``` + + + + +## Sample prompts + +Here are some sample prompts you can use to interact with the Filesystem MCP +server: + +- "List all files in the `/projects` directory" +- "Read the contents of the file `/projects/example.txt`" +- "Write 'Hello, World!' to the file `/projects/hello.txt`" + +## Recommended practices + +- Mount only the directories or files required for your AI agent's tasks to + minimize resource usage and improve performance. +- Use read-only mounts for directories or files that do not need to be modified + by the AI agent to prevent accidental changes. +- Enable network isolation to restrict the server's outbound network access, + since the filesystem MCP server does not require any network connectivity. diff --git a/versioned_docs/version-1.1/toolhive/guides-mcp/github.mdx b/versioned_docs/version-1.1/toolhive/guides-mcp/github.mdx new file mode 100644 index 00000000..ca93e474 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-mcp/github.mdx @@ -0,0 +1,211 @@ +--- +title: GitHub MCP server guide +sidebar_label: GitHub +description: Using the GitHub MCP server with ToolHive for repository management. +last_update: + author: danbarr + date: 2026-02-10 +--- + +## Overview + +The official [GitHub MCP server](https://github.com/github/github-mcp-server) +provides access to the GitHub API, allowing AI agents to interact with GitHub +repositories, issues, pull requests, and more. + +## Metadata + + + +## Usage + + + + +Select the `github` MCP server in the ToolHive registry. In the **Secrets** +section, add your GitHub personal access token to authenticate with the GitHub +API, or select an existing secret that contains the token. + +Review the optional environment variables to customize the server's behavior. +For example, you might want to limit the active toolsets or enable read-only +mode. Refer to the +[documentation](https://github.com/github/github-mcp-server?tab=readme-ov-file#tool-configuration) +for the current list of toolsets. + +:::tip[Security tip] + +Enable outbound network filtering on the **Network Isolation** tab to restrict +the server's network access using the default profile contained in the registry. + +::: + +:::info[GitHub Enterprise] + +If you're working with a GitHub Enterprise instance, enter the instance URL in +the `GITHUB_HOST` environment variable and update the network isolation settings +to allow access to the enterprise domain. + +::: + + + + +Run with the default configuration. ToolHive will prompt you to enter your +GitHub personal access token: + +```bash +thv run github +``` + +Create a secret named `github` containing your GitHub personal access token and +run the server with the `--secret` flag: + +```bash +thv secret set github +thv run --secret github,target=GITHUB_PERSONAL_ACCESS_TOKEN github +``` + +Or, use the GitHub CLI to populate the secret with your token: + +```bash +gh auth token | thv secret set github +thv run --secret github,target=GITHUB_PERSONAL_ACCESS_TOKEN github +``` + +Enable [network isolation](../guides-cli/network-isolation.mdx) using the +default profile from the registry (appropriate for `github.com`) to restrict the +server's network access: + +```bash +thv run --isolate-network github +``` + +Limit the active toolsets (useful to avoid context overload) and enable +read-only mode. Refer to the +[documentation](https://github.com/github/github-mcp-server?tab=readme-ov-file#tool-configuration) +for the current list of toolsets. + +```bash +thv run -e GITHUB_TOOLSETS=repos,issues,pull_requests -e GITHUB_READ_ONLY=1 github +``` + +Enable the MCP server's dynamic tool discovery feature (currently in beta): + +```bash +thv run -e GITHUB_DYNAMIC_TOOLSETS=1 github +``` + +:::info[GitHub Enterprise] + +Create a custom permission profile for your GitHub Enterprise instance: + +```json title="github-enterprise-profile.json" +{ + "network": { + "outbound": { + "insecure_allow_all": false, + "allow_host": ["github.your-enterprise.com"], + "allow_port": [443] + } + } +} +``` + +Then run the server with the profile: + +```bash +thv run \ + -e GITHUB_HOST=https://github.your-enterprise.com \ + --isolate-network --permission-profile github-enterprise-profile.json \ + github +``` + +::: + + + + +Create a Kubernetes secret containing your GitHub personal access token: + +```bash +kubectl -n toolhive-system create secret generic github-token --from-literal=token= +``` + +Create a Kubernetes manifest to deploy the GitHub MCP server using your secret: + +```yaml {10-14} title="github.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: github + namespace: toolhive-system +spec: + image: ghcr.io/github/github-mcp-server:v0.33.0 + transport: stdio + proxyPort: 8080 + secrets: + - name: github-token + key: token + targetEnvName: GITHUB_PERSONAL_ACCESS_TOKEN +``` + +Apply the manifest to your Kubernetes cluster: + +```bash +kubectl apply -f github.yaml +``` + +To customize the server's behavior, add environment variables to the `spec` +section of your manifest. For example, to limit the active toolsets or enable +read-only mode, add: + +```yaml title="github.yaml" +spec: + # ... + env: + - name: GITHUB_TOOLSETS + value: 'repos,issues,pull_requests' + - name: GITHUB_READ_ONLY + value: '1' +``` + +Refer to the +[documentation](https://github.com/github/github-mcp-server?tab=readme-ov-file#tool-configuration) +for the current list of toolsets. + +:::info[GitHub Enterprise] + +If you're working with a GitHub Enterprise instance, add the `GITHUB_HOST` +environment variable to the `spec` section of your manifest: + +```yaml +spec: + # ... + env: + - name: GITHUB_HOST + value: 'https://github.your-enterprise.com' +``` + +::: + + + + +## Sample prompts + +Here are some sample prompts you can use to interact with the GitHub MCP server: + +- "List all repositories for the organization `my-org`" +- "Create a new issue in the repository `my-org/my-repo` with the title 'Bug + report' and the body 'There is a bug in the code'" +- "Get the latest pull requests for the repository `my-org/my-repo`" + +## Recommended practices + +- Scope your GitHub personal access token to the minimum permissions required + for your use case. +- Regularly rotate your GitHub personal access token and update the secret in + ToolHive. +- Enable network isolation to restrict the server's outbound network access. +- Limit the active toolsets to reduce context overload and improve performance, + or use dynamic tool discovery if supported by your client. diff --git a/versioned_docs/version-1.1/toolhive/guides-mcp/grafana.mdx b/versioned_docs/version-1.1/toolhive/guides-mcp/grafana.mdx new file mode 100644 index 00000000..00608a55 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-mcp/grafana.mdx @@ -0,0 +1,234 @@ +--- +title: Grafana MCP server guide +sidebar_label: Grafana +description: + Using the Grafana MCP server with ToolHive for dashboard management, + datasource queries, alerting, and incident response. +last_update: + author: danbarr + date: 2026-02-10 +--- + +## Overview + +The official [Grafana MCP server](https://github.com/grafana/mcp-grafana) +provides comprehensive access to your Grafana instance and its surrounding +ecosystem. With support for dashboards, datasource queries (Prometheus, Loki, +Pyroscope), alerting, incident management, Grafana OnCall, and Sift +investigations, this server enables AI agents to interact with your entire +observability stack. + +The server works with both local Grafana instances and Grafana Cloud, making it +ideal for tasks like troubleshooting production issues, analyzing metrics and +logs, managing dashboards, and coordinating incident response. + +## Metadata + + + +## Usage + +You'll need a Grafana service account token to authenticate with the Grafana +API. The token must have permissions for the Grafana features you want to access +(such as dashboards, datasources, or alerting). Refer to the +[Grafana service account documentation](https://grafana.com/docs/grafana/latest/administration/service-accounts/) +for details on creating tokens and configuring permissions. + + + + +Select the `grafana` MCP server in the ToolHive registry. + +In the **Secrets** section, add your Grafana service account token or select an +existing secret that contains the token. + +In the **Environment Variables** section, configure the connection to your +Grafana instance: + +- `GRAFANA_URL`: Your Grafana instance URL (for example, `http://localhost:3000` + for local instances or `https://myinstance.grafana.net` for Grafana Cloud) +- `GRAFANA_ORG_ID` (optional): The numeric organization ID if your Grafana + instance has multiple organizations + +:::tip[Security tip] + +Enable outbound network filtering on the **Network Isolation** tab to restrict +the server's network access. Update the allowed hosts to match your Grafana +instance domain. + +::: + + + + +Create a secret containing your Grafana service account token: + +```bash +thv secret set grafana-token +``` + +Run the server with your Grafana instance URL and the secret: + +```bash +thv run \ + -e GRAFANA_URL=http://localhost:3000 \ + --secret grafana-token,target=GRAFANA_SERVICE_ACCOUNT_TOKEN \ + grafana +``` + +For Grafana Cloud, use your cloud instance URL: + +```bash +thv run \ + -e GRAFANA_URL=https://myinstance.grafana.net \ + --secret grafana-token,target=GRAFANA_SERVICE_ACCOUNT_TOKEN \ + grafana +``` + +Enable [network isolation](../guides-cli/network-isolation.mdx) to restrict the +server's network access. Create a permission profile with your Grafana instance +domain: + +```json title="grafana-profile.json" +{ + "network": { + "outbound": { + "insecure_allow_all": false, + "allow_host": ["myinstance.grafana.net"], + "allow_port": [443] + } + } +} +``` + +Then run with the custom profile: + +```bash +thv run \ + -e GRAFANA_URL=https://myinstance.grafana.net \ + --secret grafana-token,target=GRAFANA_SERVICE_ACCOUNT_TOKEN \ + --isolate-network --permission-profile grafana-profile.json \ + grafana +``` + +If your Grafana instance has multiple organizations, add the `GRAFANA_ORG_ID` +environment variable with the numeric organization ID (for example, +`-e GRAFANA_ORG_ID=2`). + +:::tip[Debug mode] + +Add the `--` separator followed by `-debug` to enable detailed logging of HTTP +requests and responses: + +```bash +thv run \ + -e GRAFANA_URL=http://localhost:3000 \ + --secret grafana-token,target=GRAFANA_SERVICE_ACCOUNT_TOKEN \ + -- -debug +``` + +::: + + + + +Create a Kubernetes secret containing your Grafana service account token: + +```bash +kubectl -n toolhive-system create secret generic grafana-token \ + --from-literal=token= +``` + +Create a Kubernetes manifest to deploy the Grafana MCP server, specifying your +Grafana instance URL and referencing the secret. This example uses a Grafana +instance running in an `observability` namespace in the cluster: + +```yaml title="grafana.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: grafana + namespace: toolhive-system +spec: + image: docker.io/grafana/mcp-grafana:0.11.4 + transport: streamable-http + mcpPort: 8000 + proxyPort: 8080 + args: + - --transport + - streamable-http + env: + - name: GRAFANA_URL + value: 'http://grafana.observability.svc.cluster.local:3000' + secrets: + - name: grafana-token + key: token + targetEnvName: GRAFANA_SERVICE_ACCOUNT_TOKEN +``` + +Apply the manifest to your Kubernetes cluster: + +```bash +kubectl apply -f grafana.yaml +``` + +For Grafana Cloud, update the `GRAFANA_URL` in the manifest: + +```yaml +spec: + env: + - name: GRAFANA_URL + value: 'https://myinstance.grafana.net' +``` + +If your Grafana instance has multiple organizations, add the `GRAFANA_ORG_ID` +environment variable with the numeric organization ID: + +```yaml +spec: + env: + - name: GRAFANA_URL + value: 'http://grafana.observability.svc.cluster.local:3000' + - name: GRAFANA_ORG_ID + value: '2' +``` + + + + +## Sample prompts + +Here are some sample prompts you can use to interact with the Grafana MCP +server: + +- "Show me all dashboards related to Kubernetes monitoring" +- "Query the Prometheus datasource for CPU usage over the last hour for the + `api-service` pod" +- "Get the recent alerts that are currently firing" +- "List all open incidents and show me details for the most recent one" +- "Find error patterns in the logs from the `production` namespace using Loki" +- "Who is currently on call for the backend team schedule?" +- "Create a new incident titled 'High memory usage on production cluster' with + severity critical" +- "Show me the panel queries from the 'API Performance' dashboard" +- "Get label values for the `namespace` label from the Loki datasource" +- "List all Sift investigations from the past week" + +## Recommended practices + +- Create service accounts with least-privilege permissions. Use fine-grained + RBAC scopes to limit access to only the datasources, dashboards, and features + required for your specific use case. +- Regularly rotate service account tokens and update the secrets in ToolHive. +- Enable network isolation to restrict the server's outbound network access to + your Grafana instance domain only. +- For dashboards with large JSON configurations, use the `get_dashboard_summary` + or `get_dashboard_property` tools to minimize context window usage instead of + retrieving the full dashboard with `get_dashboard_by_uid`. +- When working with multi-organization setups, always specify the + `GRAFANA_ORG_ID` to ensure operations target the correct organization. +- Enable [telemetry](../guides-cli/telemetry-and-metrics.mdx) to monitor API + calls and track which Grafana resources are being accessed. +- For production deployments, consider using the debug mode temporarily to + troubleshoot connection or permission issues, but disable it once everything + is working correctly. diff --git a/versioned_docs/version-1.1/toolhive/guides-mcp/k8s.mdx b/versioned_docs/version-1.1/toolhive/guides-mcp/k8s.mdx new file mode 100644 index 00000000..ae43d01a --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-mcp/k8s.mdx @@ -0,0 +1,225 @@ +--- +title: Kubernetes MCP server guide +sidebar_label: Kubernetes (MKP) +description: Using the Kubernetes (MKP) MCP server with ToolHive for cluster management. +last_update: + author: danbarr + date: 2026-02-10 +--- + +## Overview + +An MCP server that interacts with Kubernetes clusters, enabling you to manage +and automate tasks within your Kubernetes environment. + +This server is based on the +[MKP (Model Kontext Protocol)](https://github.com/StacklokLabs/mkp) project, +which is a native Go implementation of an MCP server for Kubernetes that +directly interacts with the Kubernetes API. + +## Metadata + + + +## Usage + + + + +Select the `k8s` MCP server in the ToolHive registry. The server requires +Kubernetes cluster access, so you'll need to configure authentication. + +In the **Volumes** section, mount your kubeconfig file to provide cluster +access. The most common setup is: + +- **Host path**: `/home/$USER/.kube` (your local kubeconfig directory) +- **Container path**: `/home/nonroot/.kube` +- **Read only access** (recommended for security) + +Alternatively, if you have a specific kubeconfig file, mount it directly: + +- **Host path**: `/path/to/your/kubeconfig` +- **Container path**: `/home/nonroot/.kube/config` +- **Read only access** + +:::note[Write Operations] + +By default, the server runs in read-only mode. To enable write operations (like +applying resources), add the `--read-write=true` argument in the **Command +arguments** section. Use with caution in production environments. + +::: + +:::tip[Security tip] + +Enable outbound network filtering on the **Network Isolation** tab to restrict +the server's network access to your Kubernetes cluster endpoints only. Add your +cluster's API server host and port (usually 443 or 6443) to the allowed list. + +::: + + + + +Run with the default configuration, mounting your local `.kube` directory as +read-only: + +```bash +thv run --volume $HOME/.kube:/home/nonroot/.kube:ro k8s +``` + +Mount a specific kubeconfig file: + +```bash +thv run --volume /path/to/kubeconfig:/home/nonroot/.kube/config:ro k8s +``` + +Enable write operations (create/update/delete resources) with the `--read-write` +flag: + +```bash +thv run --volume $HOME/.kube:/home/nonroot/.kube:ro \ + k8s -- --read-write=true +``` + +Disable resource discovery to reduce context size in large clusters: + +```bash +thv run --volume $HOME/.kube:/home/nonroot/.kube:ro \ + --arg --serve-resources=false \ + k8s +``` + +Enable [network isolation](../guides-cli/network-isolation.mdx) to restrict +network access to your Kubernetes cluster only. Create a custom permission +profile: + +```json title="k8s-cluster-profile.json" +{ + "network": { + "outbound": { + "insecure_allow_all": false, + "allow_host": ["your-cluster-endpoint.com", "kubernetes.default.svc"], + "allow_port": [443, 6443] + } + } +} +``` + +Then run with network isolation: + +```bash +thv run --volume $HOME/.kube:/home/nonroot/.kube:ro \ + --isolate-network --permission-profile k8s-cluster-profile.json \ + k8s +``` + + + + +This example follows the recommended practice of running the MKP MCP server +inside the cluster it's managing. A service account is used for authentication. + +Create a Kubernetes manifest to deploy the MKP server. This example creates a +service account with `cluster-admin` permissions for simplicity. In production, +create a custom ClusterRole with only the minimum permissions required. + +```yaml title="mkp.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: mkp + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/mkp/server:0.3.0 + transport: streamable-http + mcpPort: 8080 + proxyPort: 8080 + serviceAccount: mkp-sa +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: mkp-sa + namespace: toolhive-system +--- +# NOTE: This ClusterRoleBinding uses cluster-admin for example purposes only. +# In production, you should create a custom ClusterRole with the minimum +# permissions required by your MCP server instead of using cluster-admin. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: mkp-sa-cluster-admin +subjects: + - kind: ServiceAccount + name: mkp-sa + namespace: toolhive-system +roleRef: + kind: ClusterRole + name: cluster-admin + apiGroup: rbac.authorization.k8s.io +``` + +Apply the manifest to your Kubernetes cluster: + +```bash +kubectl apply -f mkp.yaml +``` + +To customize the server's behavior, add CLI arguments to the `spec` section of +your manifest. For example, to enable write operations: + +```yaml title="mkp.yaml" +spec: + # ... + args: + - '--read-write=true' +``` + +:::info[Configure tools] + +To filter or rename the tools exposed by your MCP server on Kubernetes, use the +MCPToolConfig CRD and reference it from your MCPServer with the `toolConfigRef` +field. See +[Configure tools for MCP servers on Kubernetes](../guides-k8s/customize-tools.mdx). + +::: + + + + +## Sample prompts + +Here are some sample prompts you can use to interact with the Kubernetes MCP +server: + +- "List all pods in the default namespace" +- "Show me the status of all deployments across all namespaces" +- "Get the logs from the pod named `nginx-pod` in the default namespace" +- "Create a new deployment with 3 replicas of nginx in the production namespace" +- "Show me all services in the kube-system namespace" +- "List all nodes in the cluster and show their resource usage" +- "Get the YAML definition of the deployment named `web-app` in the staging + namespace" +- "Show me all persistent volume claims that are not bound" +- "Apply this ConfigMap to the development namespace" + +## Recommended practices + +- **Use read-only mode by default**: Only enable write operations + (`--read-write=true`) when necessary and in controlled environments. +- **Implement proper RBAC**: When using service accounts, follow the principle + of least privilege and grant only the minimum permissions required. +- **Enable network isolation**: Restrict network access to your Kubernetes API + endpoints only, especially in production environments. +- **Monitor resource usage**: The server includes built-in rate limiting, but + monitor your cluster's API server load when using with AI agents. +- **Disable resource discovery in large clusters**: Use + `--serve-resources=false` to reduce context size and improve performance in + clusters with many resources. +- **Secure kubeconfig files**: When mounting kubeconfig files, always use + read-only mounts and ensure proper file permissions. +- **Use namespace scoping**: When possible, limit the server's access to + specific namespaces rather than cluster-wide permissions. +- **Enable audit logging**: Configure Kubernetes audit logging to track API + calls made by the MCP server for security monitoring. diff --git a/versioned_docs/version-1.1/toolhive/guides-mcp/notion-remote.mdx b/versioned_docs/version-1.1/toolhive/guides-mcp/notion-remote.mdx new file mode 100644 index 00000000..9768401d --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-mcp/notion-remote.mdx @@ -0,0 +1,212 @@ +--- +title: Notion MCP server guide +sidebar_label: Notion +description: + Using the Notion remote MCP server with ToolHive to access your Notion + workspace. +last_update: + author: danbarr + date: 2026-02-10 +--- + +## Overview + +The official [Notion remote MCP server](https://developers.notion.com/docs/mcp) +provides access to your Notion workspace through the Notion API, allowing AI +agents to search, read, create, and manage pages, databases, and other content +in your Notion workspace. + +This is a remote MCP server that uses the **Auto-Discovered** authorization +method, meaning ToolHive handles OAuth authentication automatically with minimal +configuration required. + +## Metadata + + + +## Usage + + + + +Select the `notion-remote` MCP server in the ToolHive registry. + +The server is preconfigured with the following settings: + +- **Server URL**: `https://mcp.notion.com/mcp` +- **Transport**: Streamable HTTP +- **Authorization method**: Auto-Discovered + +In the **Callback port** field, enter the port that ToolHive should use to +listen for the OAuth callback. This can be any available port on your machine. + +When you install the server, ToolHive: + +1. Discovers the OAuth endpoints automatically +2. Registers a new OAuth client with Notion +3. Opens your browser for authentication + +After you authorize access in your browser, the remote MCP server appears in +your server list with a "Running" status. + + + + +Run the Notion remote MCP server with the default configuration: + +```bash +thv run notion-remote +``` + +When you run the server, ToolHive: + +1. Discovers the OAuth endpoints automatically +2. Registers a new OAuth client with Notion +3. Opens your browser for authentication + +Customize the OAuth callback port if the default port (`8777`) is unavailable: + +```bash +thv run --remote-auth-callback-port 50051 notion-remote +``` + +After successful authentication, the server will be available to your configured +MCP clients. + +To restart the server (which triggers a new OAuth authentication flow): + +```bash +thv restart notion-remote +``` + + + + +:::note + +The ToolHive Kubernetes Operator does not currently support remote MCP servers +using dynamic OAuth authentication. Instead, you can run the +[local Notion MCP server](https://github.com/makenotion/notion-mcp-server) in +Kubernetes using a static integration key. + +::: + +Create an integration in Notion to obtain an authentication token by following +the instructions in the MCP server's +[README](https://github.com/makenotion/notion-mcp-server?tab=readme-ov-file#installation). + +Create a Kubernetes secret containing your Notion integration token +("`ntn_****`"): + +```bash +kubectl -n toolhive-system create secret generic notion-token --from-literal=token= +``` + +Create a Kubernetes manifest to deploy the Notion MCP server using your secret: + +```yaml title="notion.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: notion + namespace: toolhive-system +spec: + image: ghcr.io/stacklok/dockyard/npx/notion:2.2.1 + transport: stdio + proxyPort: 8080 + permissionProfile: + type: builtin + name: network + secrets: + - name: notion-token + key: token + targetEnvName: NOTION_TOKEN +``` + +Apply the manifest to your Kubernetes cluster: + +```bash +kubectl apply -f notion.yaml +``` + + + + +## Sample prompts + +Here are some sample prompts you can use to interact with the Notion MCP server: + +- "Search my Notion workspace for pages about 'project planning'" +- "Create a new page in my Notion workspace titled 'Meeting Notes' with today's + date" +- "Find all database entries in my Task List database where status is 'To Do'" +- "Update the page titled 'Weekly Report' with a summary of this week's + accomplishments" +- "List all pages in my 'Engineering' teamspace" +- "Create a new Notion database for tracking customer feedback with columns for + name, date, and feedback text" +- "Add a comment to the page titled 'Q4 Planning' with my feedback" +- "Search for pages created by john@example.com in the last 30 days" + +## Recommended practices + +- **Scope your access**: When authenticating, review the permissions requested + and only grant access to the workspaces you need for your use case. +- **Test with read operations**: Start with search and fetch operations to + familiarize yourself with the server's capabilities before making changes to + your workspace. +- **Use specific searches**: Instead of broad workspace searches, narrow down + results by searching within specific pages, databases, or teamspaces for + better performance. +- **Understand the data model**: Notion has a specific hierarchy (workspaces, + teamspaces, pages, databases, data sources). Understanding this structure + helps craft more effective prompts. +- **Be cautious with updates**: The MCP server can modify and delete content in + your Notion workspace. Always review changes before applying them to important + pages or databases. +- **Handle authentication errors**: If you see authentication errors, restart + the server to trigger a new OAuth flow: `thv restart notion-remote` + +## Troubleshooting + +
+OAuth authentication fails + +If OAuth authentication fails or times out: + +1. Ensure the callback port is not blocked by a firewall +2. Check that your browser allows pop-ups from ToolHive +3. Try restarting the server with `thv restart notion-remote` + +
+
+Server shows "Running" but tools don't work + +The server may have lost authentication. Restart the server to re-authenticate: + +```bash +thv restart notion-remote +``` + +
+
+Can't find content in search results + +- Notion search uses semantic search, which may return different results than + exact keyword matching +- Try using more specific search terms or filtering by page, database, or + teamspace +- Check that you have access to the content you're searching for (permissions + may limit results) + +
+
+Rate limits + +Notion's API has rate limits. If you encounter rate limit errors: + +- Reduce the frequency of requests +- Use more specific queries to reduce the amount of data retrieved +- Wait for the rate limit to reset (typically a few minutes) + +
diff --git a/versioned_docs/version-1.1/toolhive/guides-mcp/osv.mdx b/versioned_docs/version-1.1/toolhive/guides-mcp/osv.mdx new file mode 100644 index 00000000..6984b842 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-mcp/osv.mdx @@ -0,0 +1,121 @@ +--- +title: OSV MCP server guide +sidebar_label: Open Source Vulnerabilities (OSV) +description: Using the Open Source Vulnerabilities database (OSV) MCP server with ToolHive. +last_update: + author: danbarr + date: 2026-02-10 +--- + +## Overview + +The [OSV MCP server](https://github.com/StacklokLabs/osv-mcp) provides access to +the [Open Source Vulnerabilities database](https://osv.dev), which aggregates +vulnerability data from multiple sources including GitHub Security Advisories, +PyPA, RustSec, and many others. This server enables AI agents to: + +- Query vulnerabilities for specific package versions or Git commits +- Perform batch vulnerability queries across multiple packages +- Retrieve detailed vulnerability information by OSV ID +- Access comprehensive vulnerability data including severity, affected versions, + and remediation guidance + +The server supports various package ecosystems including npm, PyPI, Go modules, +Maven, NuGet, and more, making it an essential tool for security analysis and +dependency management workflows. + +## Metadata + + + +## Usage + + + + +Select the `osv` MCP server in the ToolHive registry. + +The OSV MCP server does not require any additional configuration or secrets. It +communicates directly with the public OSV API at `api.osv.dev`. + +:::tip[Security tip] + +Enable outbound network filtering on the **Network Isolation** tab to restrict +the server's network access using the default profile contained in the registry. + +::: + + + + +Run with the default configuration: + +```bash +thv run osv +``` + +Enable [network isolation](../guides-cli/network-isolation.mdx) using the +default profile from the registry to restrict the server's network access to +only the OSV API: + +```bash +thv run --isolate-network osv +``` + + + + +Create a Kubernetes manifest to deploy the OSV MCP server: + +```yaml title="osv.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: osv + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/osv-mcp/server:0.1.3 + transport: streamable-http + mcpPort: 8080 + proxyPort: 8080 +``` + +Apply the manifest to your Kubernetes cluster: + +```bash +kubectl apply -f osv.yaml +``` + + + + +## Sample prompts + +Here are some sample prompts you can use to interact with the OSV MCP server: + +- "Check if the npm package lodash version 4.17.15 has any known + vulnerabilities" +- "Query vulnerabilities for the Python package jinja2 version 2.4.1 and the Go + module github.com/gin-gonic/gin version 1.6.3" +- "Get detailed information about vulnerability GHSA-vqj2-4v8m-8vrq including + affected versions and remediation steps" +- "Check for vulnerabilities in commit hash + 6879efc2c1596d11a6a6ad296f80063b558d5e0f" +- "Scan these packages for vulnerabilities: express@4.17.1, react@16.13.0, and + django@3.0.5" +- "Look up vulnerability CVE-2021-44228 and provide details about affected + packages and severity" + +## Recommended practices + +- **Use batch queries** when checking multiple packages to reduce API calls and + improve performance. The batch query tool can handle multiple packages in a + single request. +- **Enable network isolation** to restrict the server's network access to only + the OSV API endpoints, improving security posture. +- **Specify ecosystems accurately** (npm, PyPI, Go, Maven, etc.) to ensure + accurate vulnerability matching and reduce false positives. +- **Use package URLs (PURLs)** when available for more precise package + identification across different ecosystems and registries. +- **Monitor rate limits** when performing large-scale vulnerability scans to + avoid overwhelming the OSV API service. diff --git a/versioned_docs/version-1.1/toolhive/guides-mcp/playwright.mdx b/versioned_docs/version-1.1/toolhive/guides-mcp/playwright.mdx new file mode 100644 index 00000000..6fd77ecf --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-mcp/playwright.mdx @@ -0,0 +1,273 @@ +--- +title: Playwright MCP server guide +sidebar_label: Playwright +description: Using the Playwright MCP server with ToolHive for browser automation. +last_update: + author: danbarr + date: 2026-02-10 +--- + +import useBaseUrl from '@docusaurus/useBaseUrl'; +import ThemedImage from '@theme/ThemedImage'; + +## Overview + +The official +[Playwright MCP server](https://github.com/microsoft/playwright-mcp) provides +browser automation capabilities using [Playwright](https://playwright.dev). This +server enables AI agents to interact with web pages through structured +accessibility snapshots, bypassing the need for screenshots or visually-tuned +models. + +Key features include: + +- **Fast and lightweight**: Uses Playwright's accessibility tree instead of + pixel-based input +- **LLM-friendly**: Operates purely on structured data without requiring vision + models +- **Deterministic tool application**: Avoids ambiguity common with + screenshot-based approaches +- **Multi-browser support**: Works with Chrome, Firefox, Safari (WebKit), and + Edge +- **Tab management**: Create, switch between, and manage multiple browser tabs +- **Persistent sessions**: Maintains login state and browser data between + interactions + +## Metadata + + + +## Usage + +The Playwright MCP server supports numerous command-line arguments to customize +its behavior. Common options include: + +- **Custom viewport**: Set `--viewport-size` to specify browser dimensions + (e.g., `1920,1080`) +- **Device emulation**: Use `--device` to emulate mobile devices (e.g., + `"iPhone 15"`) +- **Network isolation**: Configure `--allowed-origins` and `--blocked-origins` + to control which websites the browser can access +- **Output directory**: Specify `--output-dir` to save screenshots, PDFs, and + other files to a custom location; mount a host directory for persistence (see + examples below) + +Refer to the +[Playwright MCP server documentation](https://github.com/microsoft/playwright-mcp?tab=readme-ov-file#configuration) +for the full list of configuration options and their descriptions. + +:::note[Limitations] + +The containerized version of Playwright only supports the Chromium browser in +headless mode. + +::: + + + + +Select the `playwright` MCP server in the ToolHive registry. The server runs +with default settings that work for most use cases. + +To customize the server's behavior, add command-line arguments in the **Command +arguments** section. + +To save browser output files like screenshots and traces, mount a host directory +in the **Storage volumes** section and add the `--output-dir ` +command argument as shown in the screenshot below. + + + + + + +Run with the default configuration using Chromium in headless mode: + +```bash +thv run playwright +``` + +Emulate a mobile device for responsive testing: + +```bash {2} +thv run playwright -- --device "iPhone 15" +``` + +Restrict access to specific domains: + +```bash {2} +thv run playwright -- --allowed-origins "example.com;trusted-site.com" +``` + +Mount a host directory (e.g., `~/playwright-output`) to save browser output +files like screenshots and traces: + +```bash {3,5} +mkdir ~/playwright-output +thv run \ + --volume ~/playwright-output:/browser-output playwright \ + -- --output-dir /browser-output --save-trace --save-session +``` + +You can run multiple instances of the server with different configurations by +giving each a unique name: + +```bash {1,5} +thv run --name playwright-desktop playwright \ + -- --device "Desktop Chrome" --viewport-size 1920,1080 + +thv run --name playwright-iphone playwright \ + -- --device "iPhone 15" +``` + + + + +Create a basic Kubernetes manifest to deploy the Playwright MCP server using the +Streamable HTTP transport: + +```yaml title="playwright.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: playwright + namespace: toolhive-system +spec: + image: mcr.microsoft.com/playwright/mcp:v0.0.70 + transport: streamable-http + mcpPort: 8931 + proxyPort: 8080 + args: + - '--port' + - '8931' + - '--host' + - '0.0.0.0' + - '--allowed-hosts' + - '*' +``` + +:::note[Important] + +Don't remove the `--port 8931`, `--host 0.0.0.0`, or `--allowed-hosts "*"` +arguments. They are required to run the server using Streamable HTTP. + +::: + +Apply the manifest to your Kubernetes cluster: + +```bash +kubectl apply -f playwright.yaml +``` + +For production deployments with network restrictions, add the +`--allowed-origins` argument with a list of trusted domains: + +```yaml {18-19} title="playwright-restricted.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: playwright + namespace: toolhive-system +spec: + image: mcr.microsoft.com/playwright/mcp:v0.0.70 + transport: streamable-http + mcpPort: 8931 + proxyPort: 8080 + args: + - '--port' + - '8931' + - '--host' + - '0.0.0.0' + - '--allowed-hosts' + - '*' + - '--allowed-origins' + - 'example.com;trusted-domain.org' +``` + +Mount a persistent volume to save browser output files like screenshots and +traces: + +```yaml {18-19,24-27,30-33} title="playwright-with-volume.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: playwright + namespace: toolhive-system +spec: + image: mcr.microsoft.com/playwright/mcp:v0.0.70 + transport: streamable-http + mcpPort: 8931 + proxyPort: 8080 + args: + - '--port' + - '8931' + - '--host' + - '0.0.0.0' + - '--allowed-hosts' + - '*' + - '--output-dir' + - '/browser-output' + - '--save-trace' + - '--save-session' + podTemplateSpec: + spec: + volumes: + - name: playwright-output + persistentVolumeClaim: + claimName: playwright-output-claim + containers: + - name: mcp + volumeMounts: + - mountPath: /browser-output + name: playwright-output + readOnly: false +``` + + + + +## Sample prompts + +Here are some sample prompts you can use to interact with the Playwright MCP +server: + +- "Navigate to https://toolhive.dev and take a screenshot of the homepage" +- "Go to the login page, fill in the username field with 'testuser' and password + field with 'password123', then click the login button" +- "Open https://news.ycombinator.com and get the titles of the top 5 stories" +- "Navigate to a form on https://httpbin.org/forms/post, fill it out with sample + data, and submit it" +- "Go to https://github.com/microsoft/playwright and check if there are any + console errors on the page" +- "Take a full-page screenshot of https://example.com and save it as + 'homepage.png'" +- "Navigate to an e-commerce site, search for 'laptop', and capture information + about the first product" +- "Open multiple tabs, navigate to different websites in each, and then switch + between them" + +## Recommended practices + +- **Use accessibility-first interactions**: The server works best when you + describe elements by their accessible names, labels, or text content rather + than visual characteristics. +- **Leverage browser profiles**: For workflows requiring authentication, use + persistent profiles or storage state files to maintain login sessions. +- **Enable network restrictions**: Use `--allowed-origins` and + `--blocked-origins` to limit which domains the browser can access, especially + in production environments. +- **Monitor resource usage**: Browser automation can be resource-intensive; + consider using headless mode and limiting concurrent sessions. +- **Handle dynamic content**: Use the `browser_wait_for` tool when dealing with + content that loads asynchronously. +- **Organize output files**: Specify a custom output directory to keep + screenshots, PDFs, and traces organized. +- **Test responsively**: Use device emulation to test how web applications + behave on different screen sizes and devices. diff --git a/versioned_docs/version-1.1/toolhive/guides-registry/authentication.mdx b/versioned_docs/version-1.1/toolhive/guides-registry/authentication.mdx new file mode 100644 index 00000000..f0101226 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-registry/authentication.mdx @@ -0,0 +1,552 @@ +--- +title: Authentication configuration +description: + How to configure authentication for the ToolHive Registry Server with + anonymous and OAuth modes +--- + +The Registry server provides secure-by-default authentication, defaulting to +OAuth mode to protect your registry. You can configure authentication to fit +different deployment scenarios, from development environments to production +deployments with enterprise identity providers. + +## Authentication modes + +The server supports two authentication modes configured via the required `auth` +section in your configuration file: + +- **OAuth** (default): Secure authentication using JWT tokens from identity + providers +- **Anonymous**: No authentication (development/testing only) + +:::info[Secure by default] + +The server defaults to **OAuth mode** when no explicit auth configuration is +provided. This secure-by-default posture ensures your registry is protected +unless you explicitly choose anonymous mode for development scenarios. + +::: + +## OAuth authentication + +OAuth mode (the default) validates access tokens from identity providers. The +server supports two token formats: + +- **JWT tokens**: Validated locally using the provider's public keys (JWKS + endpoint). This is the most common format for modern identity providers. +- **Opaque tokens**: Validated via token introspection (RFC 7662) by querying + the provider's introspection endpoint. Use this when your provider issues + non-JWT tokens. + +This enables enterprise authentication with providers like Keycloak, Auth0, +Okta, Azure AD, Kubernetes service accounts, or any OAuth-compliant service. + +### Basic OAuth configuration + +```yaml title="config-oauth.yaml" +auth: + mode: oauth + oauth: + resourceUrl: https://registry.example.com + providers: + - name: keycloak + issuerUrl: https://keycloak.example.com/realms/mcp + audience: registry-api +``` + +### OAuth configuration fields + +| Field | Type | Required | Default | Description | +| ----------------- | -------- | -------- | ----------------------------------------- | -------------------------------------------------- | +| `mode` | string | Yes | `oauth` | Authentication mode (`oauth` or `anonymous`) | +| `resourceUrl` | string | Yes | - | The URL of the registry resource being protected | +| `realm` | string | No | `mcp-registry` | OAuth realm identifier | +| `scopesSupported` | []string | No | `[mcp-registry:read, mcp-registry:write]` | OAuth scopes advertised in the discovery endpoint | +| `publicPaths` | []string | No | `[]` | Additional paths accessible without authentication | +| `providers` | array | Yes | - | List of OAuth/OIDC identity providers | + +### Provider configuration fields + +| Field | Type | Required | Description | +| ------------------ | ------ | -------- | ----------------------------------------------------------------------- | +| `name` | string | Yes | Provider identifier for logging and monitoring | +| `issuerUrl` | string | Yes | OAuth/OIDC issuer URL (e.g., `https://keycloak.example.com/realms/mcp`) | +| `audience` | string | Yes | Expected audience claim in the access token | +| `jwksUrl` | string | No | JWKS endpoint URL (skips OIDC discovery if specified) | +| `introspectionUrl` | string | No | Token introspection endpoint URL for opaque token validation | +| `clientId` | string | No | OAuth client ID (for authenticated introspection requests) | +| `clientSecretFile` | string | No | Path to file containing client secret (for authenticated introspection) | +| `caCertPath` | string | No | Path to CA certificate for TLS verification | +| `authTokenFile` | string | No | Path to token file for authenticating JWKS/introspection requests | +| `allowPrivateIP` | bool | No | Allow connections to private IP addresses (for in-cluster use) | + +### Complete OAuth configuration example + +```yaml title="config-oauth-complete.yaml" +auth: + mode: oauth + oauth: + resourceUrl: https://registry.example.com + realm: mcp-registry + scopesSupported: + - mcp-registry:read + - mcp-registry:write + publicPaths: + - /custom-health + - /metrics + providers: + - name: keycloak-prod + issuerUrl: https://keycloak.example.com/realms/production + audience: registry-api + clientId: registry-client + clientSecretFile: /etc/secrets/keycloak-secret + caCertPath: /etc/ssl/certs/keycloak-ca.crt + - name: keycloak-staging + issuerUrl: https://keycloak.example.com/realms/staging + audience: registry-api-staging +``` + +### Opaque token configuration + +When your identity provider issues opaque (non-JWT) tokens, configure the +`introspectionUrl` field to enable token introspection: + +```yaml title="config-opaque-tokens.yaml" +auth: + mode: oauth + oauth: + resourceUrl: https://registry.example.com + providers: + - name: google + issuerUrl: https://accounts.google.com + introspectionUrl: https://oauth2.googleapis.com/tokeninfo + audience: 407408718192.apps.googleusercontent.com +``` + +The server automatically detects the token format and uses the appropriate +validation method—attempting JWT validation first and falling back to token +introspection if needed. + +## Kubernetes authentication + +For Kubernetes deployments, you can configure OAuth to validate service account +tokens. This provides automatic, zero-config authentication for workloads +running in the cluster. + +### Kubernetes provider configuration + +```yaml title="config-k8s-auth.yaml" +auth: + mode: oauth + oauth: + resourceUrl: https://registry.example.com + providers: + - name: kubernetes + issuerUrl: https://kubernetes.default.svc.cluster.local + jwksUrl: https://kubernetes.default.svc/openid/v1/jwks + audience: registry-server + caCertPath: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + authTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token + allowPrivateIP: true +``` + +:::info[Kubernetes-specific configuration] + +- **issuerUrl**: Use `https://kubernetes.default.svc.cluster.local` to match the + `iss` claim in Kubernetes service account tokens. +- **jwksUrl**: Specify the JWKS endpoint directly to skip OIDC discovery. +- **authTokenFile**: The server uses this token to authenticate when fetching + the JWKS from the Kubernetes API server. +- **allowPrivateIP**: Required for in-cluster communication with the API server. + +::: + +### How Kubernetes authentication works + +1. Workloads mount projected service account tokens with a specific audience +2. Clients send these tokens in the `Authorization: Bearer ` header +3. The server validates tokens using the Kubernetes API server's public keys +4. Only tokens with the correct audience (e.g., `registry-server`) are accepted + +### Kubernetes deployment example + +When deploying in Kubernetes, the service account CA certificate is +automatically mounted: + +```yaml title="deployment-k8s-auth.yaml" +apiVersion: apps/v1 +kind: Deployment +metadata: + name: registry-api +spec: + replicas: 1 + selector: + matchLabels: + app: registry-api + template: + metadata: + labels: + app: registry-api + spec: + serviceAccountName: registry-api + containers: + - name: registry-api + image: ghcr.io/stacklok/thv-registry-api:latest + args: + - serve + - --config=/etc/registry/config.yaml + volumeMounts: + - name: config + mountPath: /etc/registry/config.yaml + subPath: config.yaml + readOnly: true + # Service account token and CA cert are mounted automatically by Kubernetes + volumes: + - name: config + configMap: + name: registry-api-config +``` + +The service account token and CA certificate are automatically mounted at: + +- Token: `/var/run/secrets/kubernetes.io/serviceaccount/token` +- CA cert: `/var/run/secrets/kubernetes.io/serviceaccount/ca.crt` + +### Client workload example + +Clients that need to authenticate with the registry should mount a projected +service account token with the correct audience: + +```yaml title="client-workload.yaml" +apiVersion: v1 +kind: Pod +metadata: + name: registry-client +spec: + serviceAccountName: my-client-sa + containers: + - name: client + image: my-client-image + volumeMounts: + - name: registry-token + mountPath: /var/run/secrets/registry + readOnly: true + volumes: + - name: registry-token + projected: + sources: + - serviceAccountToken: + audience: registry-server + expirationSeconds: 3600 + path: token +``` + +The client reads the token from `/var/run/secrets/registry/token` and includes +it in the `Authorization: Bearer ` header when making requests to the +registry. + +## Provider-specific examples + +### Keycloak + +```yaml +auth: + mode: oauth + oauth: + resourceUrl: https://registry.example.com + providers: + - name: keycloak + issuerUrl: https://keycloak.example.com/realms/YOUR_REALM + audience: registry-api +``` + +The `issuerUrl` should point to your Keycloak realm. The realm name is part of +the URL path. + +### Auth0 + +```yaml +auth: + mode: oauth + oauth: + resourceUrl: https://registry.example.com + providers: + - name: auth0 + issuerUrl: https://YOUR_DOMAIN.auth0.com/ + audience: https://registry.example.com +``` + +For Auth0, the `issuerUrl` is your Auth0 domain. The `audience` should match the +API identifier configured in your Auth0 dashboard. + +### Azure AD + +```yaml +auth: + mode: oauth + oauth: + resourceUrl: https://registry.example.com + providers: + - name: azure-ad + issuerUrl: https://login.microsoftonline.com/YOUR_TENANT_ID/v2.0 + audience: api://YOUR_CLIENT_ID +``` + +For Azure AD, the `issuerUrl` includes your tenant ID, and the `audience` +typically uses the `api://` scheme with your application's client ID. + +### Okta + +```yaml +auth: + mode: oauth + oauth: + resourceUrl: https://registry.example.com + providers: + - name: okta + issuerUrl: https://YOUR_DOMAIN.okta.com/oauth2/default + audience: api://default +``` + +For Okta, the `issuerUrl` points to your Okta authorization server. Use +`/oauth2/default` for the default authorization server or +`/oauth2/YOUR_AUTH_SERVER_ID` for custom servers. + +## Anonymous authentication + +Anonymous mode disables authentication entirely, allowing unrestricted access to +all registry endpoints. This is only suitable for development, testing, or +internal deployments where authentication is handled at a different layer (e.g., +network policies, VPN, or reverse proxy). + +### Anonymous configuration + +```yaml title="config-anonymous.yaml" +auth: + mode: anonymous +``` + +:::danger[No access control] + +Anonymous mode provides **no access control**. Only use it in trusted +environments or when other security measures are in place. **Do not use +anonymous mode in production.** + +::: + +### Anonymous use cases + +- Local development and testing +- Internal deployments behind corporate firewalls +- Read-only public registries +- Environments with external authentication (reverse proxy, API gateway) + +## Default public paths + +The following endpoints are **always accessible without authentication**, +regardless of the auth mode: + +- `/health` - Health check endpoint +- `/readiness` - Readiness probe endpoint +- `/version` - Version information +- `/.well-known/*` - OAuth discovery endpoints (RFC 9728) + +You can configure additional public paths using the `publicPaths` field in your +OAuth configuration. See the +[Registry API reference](../reference/registry-api.mdx) for complete endpoint +documentation. + +## RFC 9728 OAuth discovery + +The server implements [RFC 9728](https://www.rfc-editor.org/rfc/rfc9728.html) +for OAuth Protected Resource Metadata, enabling clients to automatically +discover authentication requirements. + +### Discovery endpoint + +Clients can discover the server's OAuth configuration at: + +```text +GET /.well-known/oauth-protected-resource +``` + +### Example discovery response + +```json +{ + "resource": "https://registry.example.com", + "authorization_servers": [ + "https://keycloak.example.com/realms/production", + "https://keycloak.example.com/realms/staging" + ], + "scopes_supported": ["mcp-registry:read", "mcp-registry:write"], + "bearer_methods_supported": ["header"], + "resource_documentation": "https://docs.example.com/registry" +} +``` + +This allows OAuth clients to automatically configure themselves without manual +setup, improving interoperability and reducing configuration errors. + +When a request fails authentication, the server returns a `WWW-Authenticate` +header that includes a link to the discovery endpoint, helping clients locate +the authentication requirements. + +## Testing authentication + +### Using curl with a bearer token + +```bash +TOKEN="your-jwt-token-here" + +curl -H "Authorization: Bearer $TOKEN" \ + https://registry.example.com/default/v0.1/servers +``` + +### Using kubectl with Kubernetes service accounts + +Use `kubectl create token` to generate a token with the correct audience: + +```bash +# Create a token with the registry-server audience +TOKEN=$(kubectl create token \ + -n \ + --audience=registry-server) + +# Make authenticated request +curl -H "Authorization: Bearer $TOKEN" \ + https://registry.example.com/registry/v0.1/servers +``` + +:::tip[Projected tokens vs kubectl create token] + +For automated workloads, use projected service account tokens (see +[Client workload example](#client-workload-example)). The `kubectl create token` +command is useful for manual testing and debugging. + +::: + +### Testing token validation + +To verify your token is valid: + +1. Decode the JWT at [jwt.io](https://jwt.io/) (don't paste production tokens!) +2. Check the `iss` (issuer) matches your configured `issuerUrl` +3. Check the `aud` (audience) matches your configured `audience` +4. Check the `exp` (expiration) is in the future + +## Choosing an authentication mode + +| Mode | Security | Complexity | Best for | +| --------- | -------- | ---------- | ----------------------------------------------- | +| OAuth | High | Medium | Production deployments, enterprise environments | +| Anonymous | None | None | Development, testing, internal trusted networks | + +**Recommendations:** + +- **Production deployments**: Always use OAuth mode with your organization's + identity provider +- **Kubernetes deployments**: Use OAuth with Kubernetes provider for automatic + authentication +- **Development/testing**: Use anonymous mode for local development only +- **Public read-only registries**: Use OAuth mode with rate limiting; avoid + anonymous in production + +## Security considerations + +### Token validation + +All OAuth providers validate: + +- Token expiration (`exp` claim) +- Audience claim (`aud`) matches configuration +- Issuer (`iss`) matches the configured provider + +For JWT tokens, signature verification uses the provider's public keys (fetched +from the issuer's JWKS endpoint). For opaque tokens, the server queries the +configured `introspectionUrl` to validate the token. + +### HTTPS requirements + +Always use HTTPS in production to protect tokens in transit: + +```yaml +auth: + mode: oauth + oauth: + resourceUrl: https://registry.example.com # Use HTTPS + providers: + - issuerUrl: https://keycloak.example.com/realms/mcp # Use HTTPS +``` + +### Token storage + +- Never log or persist JWT tokens in plaintext +- Use short-lived tokens when possible (e.g., 1 hour) +- Implement token refresh flows for long-running clients +- Rotate client secrets regularly if using `clientSecretFile` + +### Custom CA certificates + +If your identity provider uses a custom CA certificate, specify the `caCertPath` +in your provider configuration: + +```yaml +providers: + - name: internal-keycloak + issuerUrl: https://keycloak.internal.example.com/realms/mcp + audience: registry-api + caCertPath: /etc/ssl/certs/internal-ca.crt +``` + +## Troubleshooting + +### 401 Unauthorized errors + +If you receive 401 Unauthorized responses: + +1. **Verify the token is valid**: Check expiration, issuer, and audience claims +2. **Check the Authorization header**: Must be `Authorization: Bearer ` +3. **Verify issuer URL**: Must exactly match the `iss` claim in your token +4. **Check audience**: The `aud` claim must match your configured `audience` +5. **Review server logs**: Look for specific validation error messages + +### Token validation errors + +If you see authentication failures in server logs: + +1. **Issuer URL accessibility**: Verify the server can reach the issuer's JWKS + endpoint (typically `${issuerUrl}/.well-known/openid-configuration`) +2. **Network connectivity**: Check DNS resolution and firewall rules +3. **CA certificates**: If using custom CAs, ensure `caCertPath` is correct +4. **Token expiration**: Ensure your token hasn't expired (check `exp` claim) + +### Provider connectivity issues + +If the server cannot connect to the OAuth provider: + +1. Verify network connectivity to the issuer URL from the server +2. Check DNS resolution for the provider's domain +3. Ensure firewall rules allow outbound HTTPS (port 443) to the provider +4. For Kubernetes providers, verify the API server is reachable at + `https://kubernetes.default.svc` +5. Check CA certificate configuration if using self-signed certificates + +### Multiple providers not working + +If tokens from some providers work but others don't: + +1. Verify each provider's configuration independently using curl +2. Check that audience claims differ between providers if needed (or are + intentionally the same) +3. Ensure issuer URLs are correct and include the full path (including realm for + Keycloak) +4. Review server logs to identify which specific provider validation is failing +5. Test each provider's JWKS endpoint accessibility: + `curl ${issuerUrl}/.well-known/openid-configuration` + +## Next steps + +- [Set up the database](./database.mdx) for production storage and migrations +- [Configure telemetry](./telemetry-metrics.mdx) for distributed tracing and + metrics collection diff --git a/versioned_docs/version-1.1/toolhive/guides-registry/authorization.mdx b/versioned_docs/version-1.1/toolhive/guides-registry/authorization.mdx new file mode 100644 index 00000000..a51a3964 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-registry/authorization.mdx @@ -0,0 +1,429 @@ +--- +title: Authorization +description: + Configure role-based access control and claims-based authorization for the + Registry Server +--- + +The Registry server provides a claims-based authorization model that controls +who can manage sources, registries, and entries. Authorization builds on top of +[authentication](./authentication.mdx) — you need OAuth authentication enabled +before configuring authorization. + +## How authorization works + +When a client accesses registry data, the server checks three layers in order: + +1. **Registry claims** (access gate) — can the caller access this registry at + all? If the registry has claims and the caller's JWT doesn't satisfy them, + the server returns `403 Forbidden` before any data is returned. +2. **Entry claims** (visibility filter) — which entries can the caller see? Each + entry can carry claims inherited from its source (for synced sources) or set + explicitly (via publish payload or Kubernetes annotation). Only entries whose + claims the caller's JWT satisfies are included in the response. +3. **Role checks** (admin operations) — can the caller perform this write + operation? Publishing, deleting, and managing sources/registries require + specific [roles](#configure-roles). + +When a caller makes an API request, the server: + +1. Extracts the caller's claims from their JWT token +2. Resolves the caller's roles based on those claims and the `authz` + configuration +3. Checks whether the caller's role permits the operation +4. Checks whether the caller's claims satisfy the resource's claims + +```mermaid +flowchart LR + JWT["JWT token"] --> Claims["Extract claims"] + Claims --> Roles["Resolve roles"] + Roles --> RoleCheck{"Role\npermitted?"} + RoleCheck -->|Yes| ClaimCheck{"Claims\nsatisfied?"} + RoleCheck -->|No| Deny403["403 Forbidden"] + ClaimCheck -->|Yes| Allow["Allow"] + ClaimCheck -->|No| Deny403 +``` + +## Configure roles + +Define roles in the `auth.authz.roles` section of your configuration file. Each +role maps to a list of claim rules — if a caller's JWT claims match any rule in +the list, they are granted that role. + +```yaml title="config-authz.yaml" +auth: + mode: oauth + oauth: + resourceUrl: https://registry.example.com + providers: + - name: keycloak + issuerUrl: https://keycloak.example.com/realms/mcp + audience: registry-api + # highlight-start + authz: + roles: + superAdmin: + - role: 'super-admin' + manageSources: + - org: 'acme' + role: 'admin' + manageRegistries: + - org: 'acme' + role: 'admin' + manageEntries: + - role: 'writer' + # highlight-end +``` + +### Available roles + +| Role | Grants access to | +| ------------------ | ------------------------------------------------------------- | +| `superAdmin` | All operations; bypasses all claim checks | +| `manageSources` | Create, update, delete, and list sources via the admin API | +| `manageRegistries` | Create, update, delete, and list registries via the admin API | +| `manageEntries` | Publish and delete MCP server versions and skills | + +### Role rule matching + +Each role is defined as a list of claim maps. A caller is granted the role if +their JWT claims match **any** map in the list (OR logic). Within a single map, +**all** key-value pairs must match (AND logic). + +```yaml title="Example: grant manageSources to org admins OR platform leads" +authz: + roles: + manageSources: + # Rule 1: any admin in the acme org + - org: 'acme' + role: 'admin' + # Rule 2: anyone with the platform-lead role + - role: 'platform-lead' +``` + +Claim values can be strings or arrays. When a JWT claim is an array (for +example, `role: ["admin", "writer"]`), the server checks whether any element +matches the required value. A rule with `role: "admin"` would match this JWT +because `"admin"` is one of the array elements. + +### Super-admin role + +The `superAdmin` role bypasses **all** claim checks across the entire server. A +super-admin can: + +- Access any registry regardless of its claims +- See all entries regardless of source or entry claims +- Manage any source or registry, even those with claims outside their JWT +- Publish and delete entries without claim validation + +Use this role sparingly and only for platform operators who need unrestricted +access. + +## Configure claims on sources and registries + +Claims are key-value pairs attached to sources and registries in your +configuration file. They act as access boundaries — only callers whose JWT +claims satisfy the resource's claims can access it. + +### Source claims + +For synced sources (Git, API, File), claims on a source are **inherited by all +entries** during sync. Every MCP server or skill ingested from that source +carries the source's claims. + +For Kubernetes and managed sources, source claims control who can manage the +source via the admin API but are **not** inherited by entries. Kubernetes +entries get claims from the +[`authz-claims` annotation](./configuration.mdx#per-entry-claims-via-annotation) +on each CRD. Managed source entries get claims from the publish request payload. + +```yaml title="config-source-claims.yaml" +sources: + - name: platform-tools + format: upstream + git: + repository: https://github.com/acme/platform-tools.git + branch: main + path: registry.json + syncPolicy: + interval: '30m' + # highlight-start + claims: + org: 'acme' + team: 'platform' + # highlight-end + + - name: data-tools + format: upstream + git: + repository: https://github.com/acme/data-tools.git + branch: main + path: registry.json + syncPolicy: + interval: '30m' + claims: + org: 'acme' + team: 'data' +``` + +With this configuration: + +- Entries from `platform-tools` are visible only to callers with `org: "acme"` + **and** `team: "platform"` in their JWT +- Entries from `data-tools` are visible only to callers with `org: "acme"` + **and** `team: "data"` in their JWT +- A super-admin sees all entries regardless of claims + +### Registry claims + +Claims on a registry act as an **access gate** for the consumer API. Before +returning any data from a registry's endpoints, the server checks that the +caller's JWT claims satisfy the registry's claims. + +```yaml title="config-registry-claims.yaml" +registries: + - name: platform + sources: ['platform-tools'] + # highlight-start + claims: + org: 'acme' + team: 'platform' + # highlight-end + + - name: public + sources: ['community-tools'] + # No claims — accessible to all authenticated users +``` + +A caller who requests `GET /platform/v0.1/servers` must have JWT claims that +include `org: "acme"` and `team: "platform"`. Otherwise, the server returns +`403 Forbidden`. + +### Claim containment + +The server uses **containment** (superset check) for claim validation: the +caller's claims must be a superset of the resource's claims. For example: + +| Resource claims | Caller JWT claims | Result | +| --------------------------------- | --------------------------------- | ------- | +| `{org: "acme"}` | `{org: "acme", team: "platform"}` | Allowed | +| `{org: "acme", team: "platform"}` | `{org: "acme"}` | Denied | +| `{}` (no claims) | `{org: "acme"}` | Allowed | +| `{org: "acme"}` | `{org: "contoso"}` | Denied | + +Registries and sources with no claims are accessible to all authenticated +callers. + +:::warning[Entries without claims are invisible when authorization is enabled] + +Entries with no claims are visible in anonymous mode and +[auth-only mode](#auth-only-mode), but **invisible** when full authorization is +enabled. The per-entry filter requires both sides to have claims for a match — +entries without claims are filtered out. To make entries visible, attach claims +to the source (for synced sources) or to individual entries (via the publish +payload or the +[`authz-claims` annotation](./configuration.mdx#per-entry-claims-via-annotation) +for Kubernetes sources). + +::: + +## Claims on published entries + +When you publish an MCP server version or skill to a managed source, you can +attach claims to the entry. The server enforces two rules: + +1. **Publish claims must be a subset of the publisher's JWT claims.** Every + claim key in the publish request must exist in the publisher's JWT with a + matching value. For example, if your JWT has + `{org: "acme", team: "platform"}`, you can publish entries with + `{org: "acme"}`, `{org: "acme", team: "platform"}`, or no claims at all — but + not with `{org: "contoso"}` (a value your JWT doesn't have). + +2. **Subsequent versions must have the same claims as the first.** Once you + publish the first version of an entry with specific claims, all future + versions must carry the exact same claims. This prevents accidentally + narrowing or broadening visibility across versions. + +```bash title="Publish a server with claims" +curl -X POST \ + https://registry.example.com/default/v0.1/publish \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "name": "my-server", + "version": "1.0.0", + "url": "https://mcp.example.com/my-server", + "description": "Team-scoped MCP server", + "claims": { + "org": "acme", + "team": "platform" + } + }' +``` + +## Admin API claim scoping + +When authorization is enabled, the admin API endpoints for managing sources and +registries are also scoped by claims: + +- **List sources/registries**: Only returns resources whose claims the caller's + JWT satisfies. +- **Get source/registry by name**: Returns `404 Not Found` (not `403`) when the + caller's claims don't match — this prevents information disclosure about + resources the caller cannot access. +- **List source/registry entries**: `GET /v1/sources/{name}/entries` and + `GET /v1/registries/{name}/entries` return the raw entries (servers and skills + with versions and claims) in a source or registry. These endpoints follow the + same claim scoping — the parent source or registry must be accessible to the + caller. +- **Create source/registry**: The request claims must be a subset of the + caller's JWT claims. +- **Update/delete source/registry**: The caller's JWT claims must satisfy the + existing resource's claims. + +## Auth-only mode + +When OAuth authentication is enabled but the `auth.authz` block is omitted from +your configuration, the server runs in **auth-only mode**. In this mode: + +- Callers must still authenticate with a valid JWT token +- All claims-based filtering is disabled — every authenticated caller sees all + entries regardless of source or registry claims +- Role checks are not enforced (no roles are resolved) +- The server logs a warning at startup: + `Authorization not configured, per-entry claims filtering disabled (auth-only mode)` + +Auth-only mode is useful when you need identity verification without +multi-tenant visibility controls — for example, a single-team deployment where +all authenticated users should have the same access. + +To enable full authorization, add the `auth.authz` block with +[role definitions](#configure-roles). + +## Anonymous mode + +When authentication is set to `anonymous`, all authorization checks are +bypassed. There are no JWT claims to validate, so all sources, registries, and +entries are accessible without restriction. This is suitable for development and +testing environments only. + +## Check your identity and permissions + +Use the `GET /v1/me` endpoint to verify your authenticated identity and resolved +roles: + +```bash +curl -H "Authorization: Bearer $TOKEN" \ + https://registry.example.com/v1/me +``` + +```json title="Example response" +{ + "subject": "user@example.com", + "roles": ["manageSources", "manageEntries"] +} +``` + +This is useful for debugging authorization issues — you can confirm which roles +your JWT grants and whether the expected claims are present. The endpoint +returns `401 Unauthorized` in anonymous mode since there is no identity to +report. + +## Complete example + +This example shows a multi-team setup with full RBAC and claims-based scoping: + +```yaml title="config-multi-tenant.yaml" +sources: + - name: platform-tools + format: upstream + git: + repository: https://github.com/acme/platform-tools.git + branch: main + path: registry.json + syncPolicy: + interval: '30m' + claims: + org: 'acme' + team: 'platform' + + - name: data-tools + format: upstream + git: + repository: https://github.com/acme/data-tools.git + branch: main + path: registry.json + syncPolicy: + interval: '30m' + claims: + org: 'acme' + team: 'data' + + - name: shared + managed: {} + +registries: + - name: platform + sources: ['platform-tools', 'shared'] + claims: + org: 'acme' + team: 'platform' + + - name: data + sources: ['data-tools', 'shared'] + claims: + org: 'acme' + team: 'data' + +auth: + mode: oauth + oauth: + resourceUrl: https://registry.example.com + providers: + - name: keycloak + issuerUrl: https://keycloak.example.com/realms/mcp + audience: registry-api + authz: + roles: + superAdmin: + - role: 'super-admin' + manageSources: + - org: 'acme' + role: 'admin' + manageRegistries: + - org: 'acme' + role: 'admin' + manageEntries: + - role: 'writer' +``` + +With this configuration: + +- **Platform team members** (JWT with `org: "acme"`, `team: "platform"`) can + access the `platform` registry and see entries from `platform-tools` and + `shared`. +- **Data team members** (JWT with `org: "acme"`, `team: "data"`) can access the + `data` registry and see entries from `data-tools` and `shared`. +- **Writers** (JWT with `role: "writer"`) can publish to the `shared` managed + source. +- **Admins** (JWT with `org: "acme"`, `role: "admin"`) can manage sources and + registries within the `acme` org. +- **Super-admins** (JWT with `role: "super-admin"`) can access and manage + everything. + +Entries published to the `shared` source without claims are visible through any +registry that includes it, subject only to the registry-level claims gate. To +restrict visibility further, attach claims when +[publishing entries](#claims-on-published-entries). + +## Next steps + +- [Configure authentication](./authentication.mdx) to set up OAuth providers +- [Configure sources and registries](./configuration.mdx) to set up your data + sources +- [Manage skills](./skills.mdx) to publish and discover reusable skills + +## Related information + +- [Registry server introduction](./intro.mdx) - architecture and features + overview diff --git a/versioned_docs/version-1.1/toolhive/guides-registry/configuration.mdx b/versioned_docs/version-1.1/toolhive/guides-registry/configuration.mdx new file mode 100644 index 00000000..dee635b3 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-registry/configuration.mdx @@ -0,0 +1,526 @@ +--- +title: Configuration +description: How to configure the ToolHive Registry Server with registry and sync policies +--- + +All configuration is done via YAML files. The server requires a `--config` flag +pointing to a YAML configuration file. + +## Configuration file structure + +```yaml title="config.yaml" +# Registry name/identifier (optional, defaults to "default") +registryName: my-registry + +# Registries configuration (required, can have multiple registries) +registries: + - name: toolhive + # Data format: upstream or toolhive (see below) + format: upstream + + # Git repository configuration + git: + repository: https://github.com/stacklok/toolhive-catalog.git + branch: main + path: pkg/catalog/toolhive/data/registry-upstream.json + + # Per-registry automatic sync policy (required for synced registries) + syncPolicy: + # Sync interval (e.g., "30m", "1h", "24h") + interval: '30m' + + # Optional: Per-registry server filtering + filter: + names: + include: ['official/*'] + exclude: ['*/deprecated'] + tags: + include: ['production'] + exclude: ['experimental'] + +# Authentication configuration (required) +# See authentication.mdx for detailed configuration options +auth: + mode: anonymous + +# Database configuration (required) +database: + host: localhost + port: 5432 + user: registry + database: registry + sslMode: require + maxOpenConns: 25 + maxIdleConns: 5 + connMaxLifetime: '5m' + maxMetaSize: 262144 + # Optional: dynamic authentication (alternative to pgpass) + # dynamicAuth: + # awsRdsIam: + # region: us-east-1 +``` + +## Command-line flags + +| Flag | Description | Required | Default | +| ------------- | ------------------------------------------- | -------- | ------- | +| `--config` | Path to YAML configuration file | Yes | - | +| `--address` | Server listen address | No | `:8080` | +| `--auth-mode` | Override auth mode (`anonymous` or `oauth`) | No | - | + +## Registry data formats + +The `format` field specifies the JSON schema format for the registry data: + +- **`upstream`**: The official + [upstream MCP registry format](../reference/registry-schema-upstream.mdx), + used by the MCP Registry API and recommended for new registries. +- **`toolhive`**: The + [ToolHive-native format](../reference/registry-schema-toolhive.mdx), used by + the built-in ToolHive registry. + +## Registries + +The server supports five registry types, each with its own configuration +options. You can configure multiple registries in a single server instance. + +### Git repository source + +Clone and sync from Git repositories. Ideal for version-controlled registries. + +:::note + +When the registry server clones a Git repository, it stores the local copy in +`/data`. Mounting a persistent volume there is optional but recommended for +production deployments. Without it, the registry server re-clones the repository +on every container restart, adding startup latency and increasing network usage. + +::: + +```yaml title="config-git.yaml" +registries: + - name: toolhive + format: toolhive + git: + repository: https://github.com/stacklok/toolhive-catalog.git + branch: main + path: pkg/catalog/toolhive/data/registry-legacy.json + syncPolicy: + interval: '30m' +``` + +**Configuration options:** + +- `repository` (required): Git repository URL +- `branch` (optional): Branch name to use (defaults to `main`) +- `tag` (optional): Tag name to pin to a specific version +- `commit` (optional): Commit SHA to pin to a specific commit +- `path` (optional): Path to the registry file within the repository (defaults + to `registry.json`) +- `auth` (optional): Authentication for private repositories (see below) + +:::tip + +You can use `branch`, `tag`, or `commit` to pin to a specific version. If +multiple are specified, `commit` takes precedence over `tag`, which takes +precedence over `branch`. + +::: + +#### Private repository authentication + +To access private Git repositories, configure the `auth` section with your +credentials: + +```yaml title="config-git-private.yaml" +registries: + - name: private-registry + format: toolhive + git: + repository: https://github.com/my-org/private-registry.git + branch: main + path: registry.json + # highlight-start + auth: + username: oauth2 + passwordFile: /secrets/git/token + # highlight-end + syncPolicy: + interval: '30m' +``` + +**Authentication options:** + +- `auth.username` (required with `passwordFile`): Git username for HTTP Basic + authentication. For GitHub and GitLab, use `oauth2` as the username when + authenticating with a personal access token (PAT). +- `auth.passwordFile` (required with `username`): Absolute path to a file + containing the Git password or token. Whitespace is trimmed from the file + content. + +:::warning + +Both `username` and `passwordFile` must be specified together. If only one is +provided, the configuration will fail validation. + +::: + +**Using with Kubernetes secrets:** + +In Kubernetes deployments, mount a secret containing your Git token and +reference the mount path: + +```yaml title="registry-deployment.yaml" +apiVersion: v1 +kind: Secret +metadata: + name: git-credentials +type: Opaque +stringData: + token: +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: registry-server +spec: + # Other deployment fields omitted, refer to the Deployment in Kubernetes guide + template: + spec: + containers: + - name: registry + volumeMounts: + - name: git-credentials + mountPath: /secrets/git + readOnly: true + - name: data + mountPath: /data + readOnly: false + volumes: + - name: git-credentials + secret: + secretName: git-credentials + items: + - key: token + path: token + - name: data + emptyDir: {} +``` + +### API endpoint source + +Sync from upstream MCP Registry APIs. Supports federation and aggregation +scenarios. + +```yaml title="config-api.yaml" +registries: + - name: mcp-upstream + format: upstream + api: + endpoint: https://registry.modelcontextprotocol.io + syncPolicy: + interval: '1h' +``` + +**Configuration options:** + +- `endpoint` (required): Base URL of the upstream MCP Registry API (without + path) +- `format` (required): Must be `upstream` for API sources + +:::note + +The server automatically appends the appropriate API paths (`/v0.1/servers`, +etc.) to the endpoint URL. + +::: + +### File source + +Read from filesystem. Ideal for local development and testing. + +```yaml title="config-file.yaml" +registries: + - name: local + format: upstream + file: + path: /data/registry.json + syncPolicy: + interval: '15m' +``` + +Alternatively, the source can be a custom URL. + +```yaml title="config-file.yaml" +registries: + - name: remote + format: upstream + file: + url: https://www.example.com/registry.json + timeout: 5s + syncPolicy: + interval: '15m' +``` + +The fields `file` and `url` are mutually exclusive. + +**Configuration options:** + +- `path` (required): Path to the registry file on the filesystem. Mutually + exclusive with `url` +- `url` (required): URL is the HTTP/HTTPS URL to fetch the registry file from. + Mutually exclusive with `file` +- `timeout` (optional): The timeout for HTTP requests when using URL, defaults + to 30s, ignored when `file` is specified + +### Managed registry + +API-managed registry for directly publishing and deleting MCP servers via the +API. Does not sync from external sources. + +```yaml title="config-managed.yaml" +registries: + - name: internal + format: upstream + managed: {} +``` + +**Configuration options:** + +- `managed` (required): Empty object to indicate managed registry type +- No sync policy required (managed registries are updated via API, not synced) + +**Supported operations:** + +- `POST /{registryName}/v0.1/publish` - Publish new server versions +- `DELETE /{registryName}/v0.1/servers/{name}/versions/{version}` - Delete + versions +- `GET` operations for listing and retrieving servers +- [Skills management](./skills.mdx) via the extensions API + +See the [Registry API reference](../reference/registry-api.mdx) for complete +endpoint documentation and request/response examples. + +### Kubernetes registry + +Discovers MCP servers from running Kubernetes deployments. Automatically creates +registry entries for deployed MCP servers in your cluster. + +:::note[Operator-managed registry] + +When using the ToolHive operator, a single Kubernetes registry named `default` +is automatically created and managed. You cannot configure additional Kubernetes +registries through the `MCPRegistry` CR or configuration file, as only one +Kubernetes registry instance is supported per Registry Server instance. + +The configuration example below is for reference when running the Registry +Server standalone (without the operator). + +::: + +By default, the Registry server discovers resources in all namespaces +(cluster-wide). You can restrict discovery to specific namespaces using the +`THV_REGISTRY_WATCH_NAMESPACE` environment variable. See +[Workload discovery](./deploy-manual.mdx#workload-discovery) for configuration +details and RBAC requirements. + +```yaml title="config-kubernetes.yaml" +registries: + - name: default + format: toolhive + kubernetes: {} +``` + +**Configuration options:** + +- `kubernetes` (required): Empty object to indicate Kubernetes registry type +- No sync policy required (Kubernetes registries query live deployments + on-demand) +- Only one Kubernetes registry is supported per Registry Server instance + +:::info[How does it work?] + +Kubernetes workload discovery works by looking for annotations in a specific set +of workloads. The types being watched are +[`MCPServer`](../guides-k8s/run-mcp-k8s.mdx), +[`MCPRemoteProxy`](../guides-k8s/remote-mcp-proxy.mdx), and +[`VirtualMCPServer`](../guides-vmcp/configuration.mdx). + +The Registry server will receive events when a resource among those listed above +is annotated with the following annotations: + +```yaml {7-16} +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: my-mcp-server + namespace: toolhive-system + annotations: + toolhive.stacklok.dev/registry-export: 'true' + toolhive.stacklok.dev/registry-title: 'Code Analysis Server' + toolhive.stacklok.dev/registry-url: 'https://mcp.example.com/servers/my-mcp-server' + toolhive.stacklok.dev/registry-description: | + Production MCP server for code analysis + toolhive.stacklok.dev/tool-definitions: + '[{"name":"analyze_code","description":"Analyze code for potential + issues","inputSchema":{"type":"object","properties":{"file":{"type":"string","description":"Path + to the file to + analyze"},"rules":{"type":"array","items":{"type":"string"},"description":"List + of rule IDs to check"}},"required":["file"]}}]' +spec: + # ... MCP server spec +``` + +| Annotation | Required | Description | +| -------------------------------------------- | -------- | --------------------------------------------------------------------------------- | +| `toolhive.stacklok.dev/registry-export` | Yes | Must be `"true"` to include in registry | +| `toolhive.stacklok.dev/registry-url` | Yes | The external endpoint URL for this server | +| `toolhive.stacklok.dev/registry-description` | Yes | Description text displayed in registry listings | +| `toolhive.stacklok.dev/registry-title` | No | Human-friendly display name for the registry entry (overrides the generated name) | +| `toolhive.stacklok.dev/tools` | No | JSON array of tool name strings (e.g., `["get_weather","get_forecast"]`) | +| `toolhive.stacklok.dev/tool-definitions` | No | JSON array of tool definitions with MCP tool metadata (name, description, schema) | + +**Tool definitions format:** + +The `tool-definitions` annotation accepts a JSON array containing tool metadata +that follows the MCP specification. Each tool definition can include: + +- `name` (required): Tool identifier +- `description`: Human-readable description of what the tool does +- `inputSchema`: JSON Schema describing the tool's input parameters +- `outputSchema`: JSON Schema describing the tool's output format +- `annotations`: Additional metadata about the tool + +The Registry server validates JSON syntax only. Schema validation is performed +by the operator during resource reconciliation. If the annotation contains +invalid JSON, a warning is logged and the tool definitions are omitted from the +registry, but the server is still registered normally. + +Tool definitions appear in the Registry API response under the server's registry +URL within the publisher-provided metadata: + +```json +{ + "_meta": { + "io.modelcontextprotocol.registry/publisher-provided": { + "io.github.stacklok": { + "https://mcp.example.com/servers/my-mcp-server": { + "tool_definitions": [...] + } + } + } + } +} +``` + +The registry URL from the `registry-url` annotation becomes a key in the JSON +structure. + +This feature requires the Registry server to be granted access to those +resources via a Service Account, check the details in the +[deployment section](./deploy-manual.mdx#workload-discovery). + +::: + +**Use cases:** + +- Discover MCP servers deployed via ToolHive Operator +- Automatically expose running MCP servers to end users +- Separate MCP server development from user consumption + +## Sync policy + +Configure automatic synchronization of registry data on a per-registry basis. +Only applicable to synced registries (Git, API, File). Not required for managed +or Kubernetes registries. + +```yaml +registries: + - name: toolhive + git: + repository: https://github.com/stacklok/toolhive-catalog.git + branch: main + path: pkg/catalog/toolhive/data/registry-legacy.json + syncPolicy: + # Sync interval (e.g., "30m", "1h", "24h") + interval: '30m' +``` + +The `interval` field specifies how often the server should fetch updates from +the registry. Use Go duration format (e.g., `"30m"`, `"1h"`, `"24h"`). + +:::note + +Sync policy is per-registry and must be specified within each registry +configuration. Managed and Kubernetes registries do not require sync policies. + +::: + +## Server filtering + +Optionally filter which servers are exposed through the API on a per-registry +basis. Only applicable to synced registries (Git, API, File). + +```yaml +registries: + - name: toolhive + git: + repository: https://github.com/stacklok/toolhive-catalog.git + branch: main + path: pkg/catalog/toolhive/data/registry-legacy.json + syncPolicy: + interval: '30m' + filter: + names: + include: ['official/*'] + exclude: ['*/deprecated'] + tags: + include: ['production'] + exclude: ['experimental'] +``` + +**Filter options:** + +- `names.include`: List of name patterns to include (supports wildcards) +- `names.exclude`: List of name patterns to exclude (supports wildcards) +- `tags.include`: List of tags that servers must have +- `tags.exclude`: List of tags that servers must not have + +:::note + +Filters are per-registry and specified within each registry configuration. + +::: + +## Authentication configuration + +The server supports multiple authentication modes to fit different deployment +scenarios. All authentication configuration is done via the `auth` section in +your configuration file. + +Available modes: + +- **Anonymous**: No authentication (development/testing) +- **OAuth with Kubernetes**: Service account token validation +- **OAuth with generic providers**: Integration with Keycloak, Auth0, Okta, etc. + +See the [Authentication configuration](./authentication.mdx) guide for detailed +information on configuring each mode. + +## Database configuration + +The server requires a PostgreSQL database for storing registry state and +metadata. See the [Database configuration](./database.mdx) guide for detailed +information. + +## Telemetry configuration + +The server supports OpenTelemetry for comprehensive observability, including +distributed tracing and metrics collection. See the +[Telemetry and metrics](./telemetry-metrics.mdx) guide for detailed information. + +## Next steps + +- [Configure authentication](./authentication.mdx) to secure access to your + registry +- [Set up the database](./database.mdx) for production storage diff --git a/versioned_docs/version-1.1/toolhive/guides-registry/database.mdx b/versioned_docs/version-1.1/toolhive/guides-registry/database.mdx new file mode 100644 index 00000000..a8cfa5ab --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-registry/database.mdx @@ -0,0 +1,311 @@ +--- +title: Database configuration +description: + How to configure PostgreSQL database storage and migrations for the ToolHive + Registry server +--- + +The Registry server requires a PostgreSQL database for storing registry state +and metadata. This enables persistence across restarts and provides a foundation +for advanced features. + +## Configuration + +### Basic database configuration + +```yaml title="config.yaml" +database: + host: localhost + port: 5432 + user: registry + database: registry + sslMode: require + maxOpenConns: 25 + maxIdleConns: 5 + connMaxLifetime: '5m' + maxMetaSize: 262144 +``` + +### Configuration fields + +| Field | Type | Required | Default | Description | +| ----------------- | ------ | -------- | --------- | -------------------------------------------------------------------------------------------------- | +| `host` | string | Yes | - | Database server hostname or IP address | +| `port` | int | Yes | - | Database server port | +| `user` | string | Yes | - | Database username for normal operations | +| `migrationUser` | string | No | `user` | Database username for running migrations (should have elevated privileges) | +| `database` | string | Yes | - | Database name | +| `sslMode` | string | No | `require` | SSL mode (`disable`, `require`, `verify-ca`, `verify-full`) | +| `maxOpenConns` | int | No | `25` | Maximum number of open connections to the database | +| `maxIdleConns` | int | No | `5` | Maximum number of idle connections in the pool | +| `connMaxLifetime` | string | No | `5m` | Maximum lifetime of a connection (e.g., "1h", "30m") | +| `maxMetaSize` | int | No | `262144` | Maximum allowed size in bytes for publisher-provided metadata extensions (256 KB) | +| `dynamicAuth` | object | No | - | Dynamic authentication configuration (see [Dynamic authentication](#dynamic-authentication) below) | + +\* Password configuration is required but has multiple sources (see Password +Security and Dynamic authentication below) + +## Password security + +The server supports secure password management with separate credentials for +migrations and normal operations. This follows the principle of least privilege +by using elevated privileges only when necessary. + +Password configuration is done using a +[Postgres Password File](https://www.postgresql.org/docs/current/libpq-pgpass.html) +and exporting the `PGPASSFILE` environment variable. + +### Recommended setup + +For production deployments, use separate database users: + +1. **Application user** (`user`): Limited privileges for normal operations + - SELECT, INSERT, UPDATE, DELETE on application tables + - No schema modification privileges + +2. **Migration user** (`migrationUser`): Elevated privileges for migrations + - CREATE, ALTER, DROP on schemas and tables + - Used only during migration operations + +### Example configuration with separate users + +```yaml title="config-production.yaml" +database: + host: db.example.com + port: 5432 + user: db_app + migrationUser: db_migrator + database: registry + sslMode: verify-full +``` + +Store passwords in a pgpass file with restricted permissions: + +```bash +# Create pgpass file (recommended location: /etc/secrets/pgpassfile) +echo "db.example.com:5432:registry:db_app:app_password" > /etc/secrets/pgpassfile +echo "db.example.com:5432:registry:db_migrator:migrator_password" >> /etc/secrets/pgpassfile + +# Mandatory: restrict permissions to 0600, will be ignored otherwise +chmod 600 /etc/secrets/pgpassfile +``` + +**Using the pgpass file:** + +Set the `PGPASSFILE` environment variable when running the server: + +```bash +# For standalone server +export PGPASSFILE=/etc/secrets/pgpassfile +thv-registry-api serve --config config.yaml + +# For Docker/Kubernetes +# Set the PGPASSFILE environment variable in your deployment configuration +# See deployment.mdx for examples +``` + +:::tip + +The pgpass file format is: `hostname:port:database:username:password` + +You can use wildcards (`*`) for any field except password. For example: + +- `*:5432:*:db_app:app_password` - matches any host or database +- `localhost:*:registry:db_app:app_password` - matches any port + +See the +[PostgreSQL documentation](https://www.postgresql.org/docs/current/libpq-pgpass.html) +for more details. + +::: + +You can find more details about user creation and initial configuration in this +[test file](https://github.com/stacklok/toolhive-registry-server/blob/301ccf4e3ad13daad28be7b669d8e5fca337ec14/cmd/thv-registry-api/app/serve_test.go#L56-L103). + +## Dynamic authentication + +As an alternative to pgpass files, the server supports dynamic credential +generation for cloud-hosted databases. + +### AWS RDS IAM authentication + +When running on AWS, you can authenticate to RDS using IAM credentials instead +of static passwords. The server generates short-lived authentication tokens +using the IAM role attached to the workload. + +```yaml title="config-aws-rds.yaml" +database: + host: my-database.123456789.us-east-1.rds.amazonaws.com + port: 5432 + user: my_app_user + database: registry + sslMode: require + dynamicAuth: + awsRdsIam: + region: us-east-1 +``` + +**Configuration options:** + +- `dynamicAuth.awsRdsIam.region` (required): The AWS region of the RDS instance. + Set to `detect` to automatically determine the region from the EC2 instance + metadata service (IMDS). + +:::note + +Dynamic authentication replaces pgpass files. The server generates +authentication tokens automatically before each connection. + +::: + +## Database migrations + +The server uses database migrations to manage schema changes. Migrations run +automatically on startup, but you can also run them manually. + +### Automatic migrations + +By default, the server runs migrations automatically when it starts: + +1. Connects to the database using the migration user credentials +2. Checks the current migration version +3. Applies any pending migrations +4. Switches to the application user for normal operations + +This ensures the database schema is always up to date. + +### Manual migrations + +You can run migrations manually using the CLI: + +#### Run migrations + +```bash +thv-registry-api migrate up --config config.yaml [--yes] +``` + +The `--yes` flag skips the confirmation prompt. + +#### Rollback migrations + +```bash +thv-registry-api migrate down --config config.yaml --num-steps N [--yes] +``` + +The `--num-steps` parameter specifies how many migration steps to roll back. + +### Migration user privileges + +The migration user needs the following privileges: + +- CREATE, ALTER, DROP on the target database +- Ability to create and modify tables, indexes, and other schema objects +- SELECT, INSERT, UPDATE, DELETE on the migration tracking table + +Example SQL to create a migration user: + +```sql +DO $$ +DECLARE + migrator_user TEXT := 'db_migrator'; + migrator_password TEXT := 'migrator_password'; + db_name TEXT := 'registry'; +BEGIN + EXECUTE format('CREATE USER %I WITH PASSWORD %L', migrator_user, migrator_password); + EXECUTE format('GRANT CONNECT ON DATABASE %I TO %I', db_name, migrator_user); + EXECUTE format('GRANT CREATE ON SCHEMA public TO %I', migrator_user); + EXECUTE format('GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA public TO %I', migrator_user); +END +$$; +``` + +### Application user privileges + +The application user needs limited privileges for normal operations: + +- SELECT, INSERT, UPDATE, DELETE on application tables +- No schema modification privileges + +Example SQL to create an application user: + +```sql +DO $$ +DECLARE + app_user TEXT := 'db_app'; + app_password TEXT := 'app_password'; + db_name TEXT := 'registry'; +BEGIN + CREATE ROLE toolhive_registry_server; + EXECUTE format('CREATE USER %I WITH PASSWORD %L', app_user, app_password); + EXECUTE format('GRANT toolhive_registry_server TO %I', app_user); + EXECUTE format('GRANT CONNECT ON DATABASE %I TO %I', db_name, app_user); +END +$$; +``` + +## SSL/TLS configuration + +Configure SSL/TLS for secure database connections: + +- `disable`: No SSL (not recommended for production) +- `require`: Require SSL (default) +- `verify-ca`: Require SSL and verify CA certificate +- `verify-full`: Require SSL and verify both CA and server hostname + +For production, use `verify-full`: + +```yaml +database: + sslMode: verify-full +``` + +## Connection pooling + +Tune connection pool settings for your workload: + +```yaml +database: + maxOpenConns: 25 # Maximum open connections + maxIdleConns: 5 # Maximum idle connections + connMaxLifetime: '5m' # Maximum connection lifetime +``` + +**Guidelines:** + +- `maxOpenConns`: Set based on your database server's connection limits +- `maxIdleConns`: Typically 20-25% of `maxOpenConns` +- `connMaxLifetime`: Set to less than your database server's connection timeout + +## Troubleshooting + +### Connection errors + +If you encounter connection errors: + +1. Verify database credentials are correct +2. Check network connectivity to the database server +3. Ensure the database server allows connections from your host +4. Verify SSL/TLS configuration matches your database server settings + +### Migration errors + +If migrations fail: + +1. Check that the migration user has sufficient privileges +2. Verify the database exists and is accessible +3. Check migration logs for specific error messages +4. Ensure no other processes are modifying the schema concurrently + +### Permission errors + +If you see permission errors during normal operations: + +1. Verify the application user has the required privileges +2. Check that migrations completed successfully +3. Ensure the application user can access all required tables + +## Next steps + +- [Configure telemetry](./telemetry-metrics.mdx) for distributed tracing and + metrics collection +- [Deploy the server](./deployment.mdx) to your Kubernetes environment diff --git a/versioned_docs/version-1.1/toolhive/guides-registry/deploy-manual.mdx b/versioned_docs/version-1.1/toolhive/guides-registry/deploy-manual.mdx new file mode 100644 index 00000000..0141a1ef --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-registry/deploy-manual.mdx @@ -0,0 +1,369 @@ +--- +title: Deploy manually +description: How to deploy the ToolHive Registry Server in Kubernetes using raw manifests +--- + +:::info + +For an alternative deployment approach using Kubernetes custom resources, see +[Deploy with the ToolHive Operator](./deploy-operator.mdx). + +::: + +Below is an example Kubernetes Deployment configuring the ToolHive Registry +Server to expose a single static registry based on a Git repository. + +This example assumes that a Postgres database is available at `db.example.com` +and the necessary users for migration and application execution are configured +and able to connect to a `registry` database. It also assumes that you have a +keycloak instance configured to act as identity provider. + +All resources are created in the `toolhive-system` namespace. This namespace +must exist before applying the deployment. + +For further details about user grants read the +[Migration user privileges](./database.mdx#migration-user-privileges) and +[Application user privileges](./database.mdx#application-user-privileges) +sections. + +{/* prettier-ignore */} +```yaml title="deployment.yaml" +apiVersion: apps/v1 +kind: Deployment +metadata: + name: registry-api + namespace: toolhive-system +spec: + replicas: 1 + selector: + matchLabels: + app: registry-api + template: + metadata: + labels: + app: registry-api + spec: + initContainers: + - name: pgpass-fixer + image: alpine:3 + command: + - /bin/sh + - -c + - cp /cfg/* /thv/ && chmod 0600 /thv/pgpass && chown 65532:65532 /thv/pgpass + volumeMounts: + - name: thv + mountPath: /thv + - name: config + mountPath: /cfg/config.yaml + subPath: config.yaml + - name: pgpass + mountPath: /cfg/pgpass + subPath: pgpass + containers: + - name: registry-api + image: ghcr.io/stacklok/thv-registry-api:latest + args: + - serve + - --config=/thv/config.yaml + env: + - name: PGPASSFILE + value: /thv/pgpass + ports: + - containerPort: 8080 + name: http + volumeMounts: + - name: thv + mountPath: /thv + readOnly: true + livenessProbe: + httpGet: + path: /health + port: 8080 + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /readiness + port: 8080 + initialDelaySeconds: 5 + periodSeconds: 5 + volumes: + - name: thv + emptyDir: {} + - name: config + configMap: + name: registry-api-config + items: + - key: config.yaml + path: config.yaml + - name: pgpass + secret: + secretName: registry-api-pgpass + items: + - key: pgpass + path: pgpass +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: registry-api-config + namespace: toolhive-system +data: + config.yaml: | + registryName: my-registry + registries: + - name: git-registry + format: toolhive + git: + repository: https://github.com/stacklok/toolhive-catalog.git + branch: main + path: pkg/catalog/toolhive/data/registry-legacy.json + syncPolicy: + interval: "15m" + auth: + mode: oauth + oauth: + resourceUrl: https://registry.example.com + providers: + - name: keycloak + issuerUrl: https://keycloak.example.com/realms/mcp + audience: registry-api + database: + host: db.example.com + port: 5432 + user: db_app + migrationUser: db_migrator + database: registry + sslMode: verify-full +--- +apiVersion: v1 +kind: Secret +metadata: + name: registry-api-pgpass + namespace: toolhive-system +type: Opaque +stringData: + pgpass: | + db.example.com:5432:registry:db_app:app_password + db.example.com:5432:registry:db_migrator:migrator_password +--- +apiVersion: v1 +kind: Service +metadata: + name: registry-api + namespace: toolhive-system +spec: + selector: + app: registry-api + ports: + - port: 80 + targetPort: 8080 + protocol: TCP + type: ClusterIP +``` + +Apply the deployment: + +```bash +kubectl apply -f deployment.yaml +``` + +## Workload discovery + +Kubernetes workload discovery works by looking for annotations in a specific set +of workloads. The types being watched are +[`MCPServer`](../guides-k8s/run-mcp-k8s.mdx), +[`MCPRemoteProxy`](../guides-k8s/remote-mcp-proxy.mdx), and +[`VirtualMCPServer`](../guides-vmcp/configuration.mdx). + +By default, the Registry server discovers resources in all namespaces +(cluster-wide). You can restrict discovery to specific namespaces by setting the +`THV_REGISTRY_WATCH_NAMESPACE` environment variable to a comma-separated list of +namespace names in your deployment: + +```yaml +env: + - name: THV_REGISTRY_WATCH_NAMESPACE + value: toolhive-system,production +``` + +When `THV_REGISTRY_WATCH_NAMESPACE` is set, only resources in the specified +namespaces are discovered. When unset, the server watches all namespaces. + +Both RBAC options below use the same ClusterRole for workload discovery and a +separate namespace-scoped Role for leader election. The difference is how the +ClusterRole is bound. + +### Cluster-wide discovery (default) + +For cluster-wide discovery, apply the following resources: + +{/* prettier-ignore */} +```yaml title="registry-rbac-cluster.yaml" +apiVersion: v1 +kind: ServiceAccount +metadata: + labels: + toolhive.stacklok.io/registry-name: registry-api + name: registry-api + namespace: toolhive-system +--- +# Manager role for workload discovery (ToolHive CRDs + services) +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + labels: + toolhive.stacklok.io/registry-name: registry-api + name: registry-api-manager +rules: + - apiGroups: + - toolhive.stacklok.dev + resources: + - mcpservers + - mcpremoteproxies + - virtualmcpservers + verbs: + - get + - list + - watch + - apiGroups: + - '' + resources: + - services + verbs: + - get + - list + - watch +--- +# Leader election role (namespace-scoped, always required) +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + labels: + toolhive.stacklok.io/registry-name: registry-api + name: registry-api-leader-election + namespace: toolhive-system +rules: + - apiGroups: + - '' + resources: + - configmaps + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - get + - list + - watch + - create + - update + - patch + - delete + - apiGroups: + - '' + resources: + - events + verbs: + - create + - patch +--- +# Leader election binding (always namespace-scoped) +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + toolhive.stacklok.io/registry-name: registry-api + name: registry-api-leader-election + namespace: toolhive-system +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: registry-api-leader-election +subjects: + - kind: ServiceAccount + name: registry-api + namespace: toolhive-system +--- +# Cluster-wide binding for the manager role +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + labels: + toolhive.stacklok.io/registry-name: registry-api + name: registry-api-manager +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: registry-api-manager +subjects: + - kind: ServiceAccount + name: registry-api + namespace: toolhive-system +``` + +### Namespace-scoped discovery + +When `THV_REGISTRY_WATCH_NAMESPACE` is set, use the same ClusterRole but bind it +with a RoleBinding in each watched namespace instead of a ClusterRoleBinding. +Create one RoleBinding per namespace: + +{/* prettier-ignore */} +```yaml title="registry-rbac-namespace.yaml" +# Use the same ServiceAccount, ClusterRole, and leader election +# Role/RoleBinding from the cluster-wide example above. +# Replace the ClusterRoleBinding with one RoleBinding per namespace: +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + labels: + toolhive.stacklok.io/registry-name: registry-api + name: registry-api-manager + namespace: toolhive-system # repeat for each watched namespace +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: registry-api-manager +subjects: + - kind: ServiceAccount + name: registry-api + namespace: toolhive-system +``` + +### Applying the service account + +Apply the service account to the registry server deployment in the +`spec.template.spec` section: + +```yaml +spec: + template: + spec: + serviceAccountName: registry-api +``` + +:::tip + +If you run multiple Registry Server instances in the same namespace, set the +`THV_REGISTRY_LEADER_ELECTION_ID` environment variable to a unique value for +each instance to avoid leader election lease conflicts. The Helm chart handles +this automatically. + +::: + +## Next steps + +- [Configure registry sources](./configuration.mdx) to set up your data sources + and sync policies +- [Set up authentication](./authentication.mdx) to secure access to your + registry +- [Configure telemetry](./telemetry-metrics.mdx) to monitor your deployment diff --git a/versioned_docs/version-1.1/toolhive/guides-registry/deploy-operator.mdx b/versioned_docs/version-1.1/toolhive/guides-registry/deploy-operator.mdx new file mode 100644 index 00000000..43d64cfe --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-registry/deploy-operator.mdx @@ -0,0 +1,730 @@ +--- +title: Deploy with the ToolHive Operator +description: + How to deploy the ToolHive Registry Server in Kubernetes using the ToolHive + Operator +--- + +## Prerequisites + +- A Kubernetes cluster (current and two previous minor versions are supported) +- Permissions to create resources in the cluster +- [`kubectl`](https://kubernetes.io/docs/tasks/tools/) configured to communicate + with your cluster +- The ToolHive operator installed in your cluster (see + [Deploy the operator](../guides-k8s/deploy-operator.mdx)) +- A PostgreSQL database + +## Overview + +The ToolHive operator deploys the Registry server in Kubernetes by creating +`MCPRegistry` resources. Alternatively, you can deploy the Registry Server +manually by following the [manual deployment instructions](./deploy-manual.mdx). + +### High-level architecture + +This diagram shows the basic relationship between components. The ToolHive +operator watches for `MCPRegistry` resources and automatically creates the +necessary infrastructure to run the Registry server. + +```mermaid +flowchart LR + subgraph K8s["Kubernetes Cluster"] + direction LR + subgraph NS["toolhive-system"] + Operator["ToolHive Operator"] + end + subgraph NS2["Registry
Namespace"] + Registry["Registry Server"] + DB[("PostgreSQL")] + end + subgraph Sources["Registry Sources"] + Git["Git Repository"] + CM["ConfigMap"] + PVC["PVC"] + API["Upstream API"] + end + end + + Operator -.->|creates| Registry + Sources -->|sync| Registry + Registry --> DB +``` + +## Create a registry + +You can create `MCPRegistry` resources in the namespaces where the ToolHive +Operator is deployed. + +See +[Deploy the operator](../guides-k8s/deploy-operator.mdx#operator-deployment-modes) +to learn about the different deployment modes. + +To create a registry, define an `MCPRegistry` resource and apply it to your +cluster. This minimal example creates a registry that syncs from the ToolHive +Git repository. + +```yaml title="my-registry.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRegistry +metadata: + name: my-registry + namespace: my-namespace # Update with your namespace +spec: + displayName: My MCP Registry + auth: + mode: anonymous + registries: + - name: toolhive + format: toolhive + git: + repository: https://github.com/stacklok/toolhive-catalog.git + branch: main + path: pkg/catalog/toolhive/data/registry-legacy.json + syncPolicy: + interval: '30m' +``` + +Apply the resource: + +```bash +kubectl apply -f my-registry.yaml +``` + +:::info[What's happening?] + +When you apply an `MCPRegistry` resource, here's what happens: + +1. The ToolHive operator detects the new resource (if it's in an allowed + namespace) +2. The operator creates the necessary RBAC resources in the target namespace +3. The operator creates a Deployment containing the Registry server pod and + service +4. The Registry server syncs data from the configured sources +5. The Registry API becomes available at the service endpoint + +::: + +## Configuring source Registries + +The `MCPRegistry` resource supports multiple registry source types. You can +configure one or more of them. Each type is mutually exclusive within a single +registry configuration. + +### Git repository source + +Clone and sync from Git repositories. Ideal for version-controlled registries. + +```yaml {9-14} title="registry-git.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRegistry +metadata: + name: git-registry +spec: + auth: + mode: anonymous + registries: + - name: toolhive + format: toolhive + git: + repository: https://github.com/stacklok/toolhive-catalog.git + branch: main + path: pkg/catalog/toolhive/data/registry-legacy.json + syncPolicy: + interval: '30m' +``` + +**Git source fields:** + +| Field | Required | Description | +| ------------ | -------- | ----------------------------------------------------- | +| `repository` | Yes | Git repository URL (HTTP/HTTPS/SSH) | +| `branch` | No | Branch name (mutually exclusive with `tag`, `commit`) | +| `tag` | No | Tag name (mutually exclusive with `branch`, `commit`) | +| `commit` | No | Commit SHA (mutually exclusive with `branch`, `tag`) | +| `path` | No | Path to registry file (default: `registry.json`) | + +:::tip + +You can use `branch`, `tag`, or `commit` to pin to a specific version. If +multiple are specified, `commit` takes precedence over `tag`, which takes +precedence over `branch`. + +::: + +### ConfigMap source + +Read from a Kubernetes ConfigMap. Ideal for registry data managed within the +cluster. + +```yaml {7-11} title="registry-configmap.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRegistry +metadata: + name: configmap-registry +spec: + auth: + mode: anonymous + registries: + - name: local + format: upstream + configMapRef: + name: registry-data + key: registry.json + syncPolicy: + interval: '15m' +``` + +The ConfigMap must exist in the same namespace as the `MCPRegistry` resource. + +### PersistentVolumeClaim source + +Read from a PersistentVolumeClaim. Useful for large registry files or shared +storage scenarios. + +```yaml {7-10} title="registry-pvc.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRegistry +metadata: + name: pvc-registry +spec: + auth: + mode: anonymous + registries: + - name: shared + format: upstream + pvcRef: + claimName: registry-data-pvc + path: registries/production.json + syncPolicy: + interval: '1h' +``` + +**PVC source fields:** + +| Field | Required | Description | +| ----------- | -------- | ----------------------------------------------------------- | +| `claimName` | Yes | Name of the PersistentVolumeClaim | +| `path` | No | Path to registry file within PVC (default: `registry.json`) | + +The PVC must exist in the same namespace as the `MCPRegistry` resource. + +### API source + +Sync from an upstream MCP Registry API. Supports federation and aggregation +scenarios. + +```yaml {7-10} title="registry-api.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRegistry +metadata: + name: api-registry +spec: + auth: + mode: anonymous + registries: + - name: upstream + format: upstream + api: + endpoint: https://registry.example.com + syncPolicy: + interval: '1h' +``` + +The controller automatically appends the appropriate API paths to the endpoint +URL. + +### Configure synchronization + +Each registry source can have its own sync policy that controls automatic +synchronization. + +```yaml +syncPolicy: + interval: '30m' # Go duration format: "1h", "30m", "24h" +``` + +### Filter registry content + +You can filter which servers are exposed through the API using name and tag +patterns. + +```yaml {13-20} title="registry-filtered.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRegistry +metadata: + name: filtered-registry +spec: + auth: + mode: anonymous + registries: + - name: toolhive + format: toolhive + git: + repository: https://github.com/stacklok/toolhive-catalog.git + branch: main + path: pkg/catalog/toolhive/data/registry-legacy.json + filter: + names: + include: + - 'official/*' + exclude: + - '*/deprecated' + tags: + include: + - production + exclude: + - experimental + syncPolicy: + interval: '30m' +``` + +## Configure database storage + +Configure PostgreSQL database storage for the Registry server. + +```yaml {17-32} title="registry-with-database.yaml" +apiVersion: v1 +kind: Secret +metadata: + name: registry-api-db-passwords +type: Opaque +stringData: + db-password: app_password + migration-password: migrator_password +--- +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRegistry +metadata: + name: production-registry +spec: + auth: + mode: anonymous + databaseConfig: + host: postgres.database.svc.cluster.local + port: 5432 + user: db_app + migrationUser: db_migrator + dbAppUserPasswordSecretRef: + name: registry-api-db-passwords + key: db-password + dbMigrationUserPasswordSecretRef: + name: registry-api-db-passwords + key: migration-password + database: registry + sslMode: verify-full + maxOpenConns: 25 + maxIdleConns: 5 + connMaxLifetime: '30m' + registries: + - name: toolhive + format: toolhive + git: + repository: https://github.com/stacklok/toolhive-catalog.git + branch: main + path: pkg/catalog/toolhive/data/registry-legacy.json + syncPolicy: + interval: '30m' +``` + +**Database configuration fields:** + +| Field | Default | Description | +| ---------------------------------- | ------------- | ------------------------------------------------- | +| `host` | `postgres` | Database server hostname | +| `port` | `5432` | Database server port | +| `user` | `db_app` | Application user (SELECT, INSERT, UPDATE, DELETE) | +| `migrationUser` | `db_migrator` | Migration user (CREATE, ALTER, DROP) | +| `dbAppUserPasswordSecretRef` | `` | Password of application user | +| `dbMigrationUserPasswordSecretRef` | `` | Password of migration user | +| `database` | `registry` | Database name | +| `sslMode` | `prefer` | SSL mode (disable, prefer, require, verify-full) | +| `maxOpenConns` | `10` | Maximum open connections | +| `maxIdleConns` | `2` | Maximum idle connections | +| `connMaxLifetime` | `30m` | Maximum connection lifetime | + +:::tip + +Credentials are internally configured using a pgpass file mounted as a secret. + +::: + +## Configure authentication + +You can configure authentication using the `authConfig` field in your +`MCPRegistry` resource. + +### Authentication modes + +| Mode | Description | Use case | +| ----------- | ----------------------------------------------- | ---------------------------- | +| `oauth` | Validates access tokens from identity providers | Production deployments | +| `anonymous` | No authentication required | Development and testing only | + +:::info[Secure by default] + +Configuring an authentication mode is mandatory, if you're not interested you +can set it to `anonymous`. + +::: + +### OAuth authentication + +OAuth mode validates JWT tokens from one or more identity providers. Configure +providers in the `authConfig.oauth.providers` array. + +```yaml title="registry-oauth.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRegistry +metadata: + name: registry + namespace: toolhive-system +spec: + displayName: 'Authenticated MCP Server Registry' + authConfig: + mode: oauth + oauth: + providers: + - name: kubernetes + issuerUrl: https://kubernetes.default.svc.cluster.local + jwksUrl: https://kubernetes.default.svc/openid/v1/jwks + audience: registry-server + caCertPath: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + authTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token + allowPrivateIP: true + registries: + - name: toolhive + format: toolhive + git: + repository: https://github.com/stacklok/toolhive-catalog.git + branch: main + path: pkg/catalog/toolhive/data/registry-legacy.json +``` + +### OAuth configuration fields + +| Field | Required | Default | Description | +| ----------------- | -------- | ----------------------------------------- | -------------------------------------------------- | +| `mode` | No | `oauth` | Authentication mode (`oauth` or `anonymous`) | +| `resourceUrl` | No | - | URL identifying this protected resource (RFC 9728) | +| `realm` | No | `mcp-registry` | Protection space identifier for WWW-Authenticate | +| `scopesSupported` | No | `[mcp-registry:read, mcp-registry:write]` | OAuth scopes supported by this resource | + +### Provider configuration fields + +| Field | Required | Description | +| ------------------ | -------- | ----------------------------------------------------------------------- | +| `name` | Yes | Unique identifier for this provider (for logging and monitoring) | +| `issuerUrl` | Yes | OIDC issuer URL (e.g., `https://accounts.google.com`) | +| `audience` | Yes | Expected audience claim in the access token | +| `jwksUrl` | No | JWKS endpoint URL (skips OIDC discovery if specified) | +| `clientId` | No | OAuth client ID for token introspection | +| `clientSecretFile` | No | Path to file containing the client secret | +| `caCertPath` | No | Path to CA certificate for TLS verification | +| `authTokenFile` | No | Path to token file for authenticating to OIDC/JWKS endpoints | +| `introspectionUrl` | No | Token introspection endpoint URL for opaque token validation (RFC 7662) | +| `allowPrivateIP` | No | Allow connections to private IP addresses (required for in-cluster) | + +### Kubernetes service account authentication + +For in-cluster deployments, you can configure OAuth to validate Kubernetes +service account tokens: + +```yaml title="registry-k8s-auth.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRegistry +metadata: + name: registry +spec: + authConfig: + mode: oauth + oauth: + providers: + - name: kubernetes + issuerUrl: https://kubernetes.default.svc.cluster.local + jwksUrl: https://kubernetes.default.svc/openid/v1/jwks + audience: registry-server + caCertPath: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + authTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token + allowPrivateIP: true +``` + +:::tip[Kubernetes provider settings] + +- **issuerUrl**: for most Kubernetes distributions, + `https://kubernetes.default.svc.cluster.local` is the correct value to match + the `iss` claim in Kubernetes service account tokens. +- **jwksUrl**: Specify directly to skip OIDC discovery (the Kubernetes API + server doesn't support standard discovery). +- **allowPrivateIP**: Required for in-cluster communication with the API server. + +::: + +### Multiple providers + +You can configure multiple OAuth providers to accept tokens from different +identity sources: + +```yaml title="registry-multi-provider.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRegistry +metadata: + name: registry +spec: + authConfig: + mode: oauth + oauth: + providers: + # Kubernetes service accounts (in-cluster workloads) + - name: kubernetes + issuerUrl: https://kubernetes.default.svc.cluster.local + jwksUrl: https://kubernetes.default.svc/openid/v1/jwks + audience: registry-server + caCertPath: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt + authTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token + allowPrivateIP: true + # External identity provider + - name: okta + issuerUrl: https://YOUR_DOMAIN.okta.com/oauth2/default + audience: registry +``` + +The server validates tokens against each provider in order until one succeeds. + +### Anonymous authentication + +For development and testing, you can disable authentication entirely: + +```yaml title="registry-anonymous.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRegistry +metadata: + name: registry +spec: + authConfig: + mode: anonymous + registries: + - name: toolhive + format: toolhive + git: + repository: https://github.com/stacklok/toolhive-catalog.git + branch: main + path: pkg/catalog/toolhive/data/registry-legacy.json +``` + +:::danger[No access control] + +Anonymous mode provides **no access control**. Only use it in trusted +environments or when other security measures are in place. **Do not use +anonymous mode in production.** + +::: + +For detailed information about authentication configuration, including +provider-specific examples for Keycloak, Auth0, Azure AD, and Okta, see the +[Authentication configuration](./authentication.mdx) guide. + +## Customize the Registry server pod + +You can customize the Registry server pod using the `podTemplateSpec` field. +This gives you full control over the pod specification. + +```yaml {6-15} title="registry-custom-pod.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRegistry +metadata: + name: custom-registry +spec: + podTemplateSpec: + spec: + containers: + - name: registry-api # This name must be "registry-api" + resources: + limits: + cpu: '500m' + memory: '512Mi' + requests: + cpu: '100m' + memory: '128Mi' + auth: + mode: anonymous + registries: + - name: toolhive + format: toolhive + git: + repository: https://github.com/stacklok/toolhive-catalog.git + branch: main + path: pkg/catalog/toolhive/data/registry-legacy.json + syncPolicy: + interval: '30m' +``` + +:::info[Container name requirement] + +When customizing containers in `podTemplateSpec`, you must use +`name: registry-api` for the main container to ensure the operator can properly +manage the Registry server. The container name is hardcoded to avoid conflict +issues with user provided containers. Mandating a container name on the Operator +side explicitly tells the Operator it is the main registry server container and +any other containers provided by the use are sidecars/init containers. + +::: + +## Check registry status + +To check the status of your registries in a specific namespace: + +```bash +kubectl -n get mcpregistries +``` + +To check registries across all namespaces: + +```bash +kubectl get mcpregistries --all-namespaces +``` + +The status displays the phase, message, and age of each registry. + +For more details about a specific registry: + +```bash +kubectl -n describe mcpregistry +``` + +### Registry phases + +| Phase | Description | +| ------------- | -------------------------------------- | +| `Pending` | The registry is being initialized | +| `Ready` | The registry is ready and operational | +| `Syncing` | The registry is currently syncing data | +| `Failed` | The registry has encountered an error | +| `Terminating` | The registry is being deleted | + +## Next steps + +Learn how to configure authentication for the Registry server in the +[Authentication configuration](./authentication.mdx) guide. + +Configure additional registry sources and filtering options in the +[Configuration](./configuration.mdx) guide. + +Discover your deployed MCP servers automatically using the +[Kubernetes registry](./configuration.mdx#kubernetes-registry) feature. Note +that the operator automatically creates a single Kubernetes registry named +`default` - you cannot configure additional Kubernetes registries. + +## Related information + +- [Kubernetes CRD reference](../reference/crd-spec.md#apiv1alpha1mcpregistry) - + Reference for the `MCPRegistry` Custom Resource Definition (CRD) +- [Deploy the operator](../guides-k8s/deploy-operator.mdx) - Install the + ToolHive operator +- [Database configuration](./database.mdx) - Configure PostgreSQL storage + +## Troubleshooting + +
+MCPRegistry resource not creating pods + +If your `MCPRegistry` resource is created but no pods appear: + +1. Ensure you created the `MCPRegistry` resource in an allowed namespace +1. Check the operator's configuration: + +```bash +helm get values toolhive-operator -n toolhive-system +``` + +1. Check the MCPRegistry status and operator logs: + +```bash +# Check MCPRegistry status +kubectl -n describe mcpregistry + +# Check operator logs +kubectl -n toolhive-system logs -l app.kubernetes.io/name=toolhive-operator + +# Verify the operator is running +kubectl -n toolhive-system get pods -l app.kubernetes.io/name=toolhive-operator +``` + +Common causes include: + +- **Operator not running**: Ensure the ToolHive operator is deployed and running +- **RBAC issues**: Check for cluster-level permission issues +- **Resource quotas**: Check if namespace resource quotas prevent pod creation + +
+ +
+Registry stuck in Pending or Syncing phase + +If the registry is stuck in `Pending` or `Syncing` phase: + +```bash +# Check registry status +kubectl -n describe mcpregistry + +# Check registry pod logs +kubectl -n logs -l app.kubernetes.io/instance= +``` + +Common causes include: + +- **Git repository inaccessible**: Verify the repository URL is correct and + accessible +- **ConfigMap/PVC doesn't exist**: Ensure referenced resources exist in the same + namespace +- **Network policies**: Check if network policies are blocking external access +- **Invalid registry file format**: Verify the registry JSON file is valid + +
+ +
+Database connection errors + +If you see database connection errors: + +```bash +# Check registry pod logs +kubectl -n logs -l app.kubernetes.io/instance= +``` + +Common causes include: + +- **Database not reachable**: Verify database host and port are correct +- **Invalid credentials**: Check that pgpass file is properly mounted +- **SSL configuration mismatch**: Verify `sslMode` matches your database + configuration +- **Permission issues**: Ensure database users have required privileges + +
+ +
+Sync failures + +If synchronization is failing: + +```bash +# Check sync status +kubectl -n get mcpregistry -o jsonpath='{.status.syncStatus}' + +# Trigger manual sync to see immediate errors +kubectl annotate mcpregistry \ + toolhive.stacklok.dev/sync-trigger="$(date +%s)" \ + --overwrite + +# Check logs +kubectl -n logs -l app.kubernetes.io/instance= +``` + +Common causes include: + +- **Source unavailable**: Git repository, API endpoint, or file is inaccessible +- **Invalid JSON format**: Registry file contains invalid JSON +- **Format mismatch**: The `format` field doesn't match the actual data format +- **Filter too restrictive**: Filters may be excluding all servers + +
diff --git a/versioned_docs/version-1.1/toolhive/guides-registry/deployment.mdx b/versioned_docs/version-1.1/toolhive/guides-registry/deployment.mdx new file mode 100644 index 00000000..81d3b029 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-registry/deployment.mdx @@ -0,0 +1,42 @@ +--- +title: Deploy the Registry Server +description: Deployment options for the ToolHive Registry Server in Kubernetes +--- + +The Registry server can be deployed in Kubernetes using three methods. Choose +the one that fits your environment: + +| Method | Description | +| ------------------------------------------------ | --------------------------------------------------------------- | +| [ToolHive Operator](#toolhive-operator) | Manage the Registry Server lifecycle through `MCPRegistry` CRDs | +| [Helm](#helm) | Deploy using the Registry Server Helm chart | +| [Manual manifests](#manual-kubernetes-manifests) | Deploy directly using raw Kubernetes manifests | + +## ToolHive Operator + +Deploy and manage the Registry server using `MCPRegistry` custom resources. The +ToolHive Operator watches for these resources and creates the necessary +infrastructure automatically. + +See [Deploy with the ToolHive Operator](./deploy-operator.mdx) for a complete +guide. + +## Helm + +A Helm chart is available for the Registry Server. Documentation for this +deployment method is coming soon. + +## Manual Kubernetes manifests + +Deploy the Registry Server directly using raw Kubernetes manifests. This +approach gives you full control over the deployment configuration. + +See [Deploy manually](./deploy-manual.mdx) for instructions. + +## Next steps + +- [Configure registry sources](./configuration.mdx) to set up your data sources + and sync policies +- [Set up authentication](./authentication.mdx) to secure access to your + registry +- [Configure telemetry](./telemetry-metrics.mdx) to monitor your deployment diff --git a/versioned_docs/version-1.1/toolhive/guides-registry/index.mdx b/versioned_docs/version-1.1/toolhive/guides-registry/index.mdx new file mode 100644 index 00000000..9290f2c7 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-registry/index.mdx @@ -0,0 +1,38 @@ +--- +title: ToolHive Registry Server +description: + How-to guides for using the ToolHive Registry Server to discover and access + MCP servers and skills. +--- + +import DocCardList from '@theme/DocCardList'; + +## Introduction + +The ToolHive Registry Server is an implementation of the official +[Model Context Protocol (MCP) Registry API specification](https://github.com/modelcontextprotocol/registry/blob/main/docs/reference/api/generic-registry-api.md). +It provides a standardized REST API for discovering and accessing MCP servers +from multiple backend sources, including Kubernetes clusters, Git repositories, +API endpoints, and local files. + +:::note + +This section covers the **Registry Server**, a standalone service you deploy +yourself. Looking for the built-in registry instead? See +[Explore the registry](../guides-cli/registry.mdx) (CLI) or +[Explore the registry](../guides-ui/registry.mdx) (UI). + +::: + +## Where to start + +- **Evaluating the Registry Server?** Read the [Introduction](./intro.mdx) for + architecture, features, and supported registry source types. +- **Ready to deploy?** See [Deployment overview](./deployment.mdx) to choose + between Kubernetes Operator and manual deployment methods. +- **Already running?** Jump to [Configuration](./configuration.mdx) or + [Authentication](./authentication.mdx). + +## Contents + + diff --git a/versioned_docs/version-1.1/toolhive/guides-registry/intro.mdx b/versioned_docs/version-1.1/toolhive/guides-registry/intro.mdx new file mode 100644 index 00000000..62902b3d --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-registry/intro.mdx @@ -0,0 +1,123 @@ +--- +title: Introduction +description: + How to use the ToolHive Registry Server to discover and access MCP servers and + skills +--- + +The ToolHive Registry Server is a standards-compliant implementation of the MCP +Registry API specification. It provides a REST API for discovering and accessing +MCP servers from multiple backend sources. + +:::note[Registry Server vs. built-in registry] + +This section covers the **Registry Server** — a standalone service you deploy +yourself for hosting and curating your own MCP server catalog. + +ToolHive also ships with a **built-in registry** for browsing and discovering +MCP servers from the default catalog. That's a different feature: see +[Explore the registry](../guides-cli/registry.mdx) (CLI) or +[Explore the registry](../guides-ui/registry.mdx) (UI). + +::: + +## How the Registry Server works + +The Registry server aggregates MCP server metadata from various sources and +exposes it through a standardized API. When you start the server, it: + +1. Loads configuration from a YAML file +2. Runs database migrations automatically +3. Immediately fetches registry data from the configured sources +4. Starts background sync coordinator for automatic updates (for synced + registries) +5. Serves MCP Registry API endpoints on the configured address + +```mermaid +flowchart LR + subgraph Sources["Registry sources"] + direction LR + Managed["Managed registry"] + API["Upstream registry"] + Kubernetes["Kubernetes cluster"] + Git["Git repository"] + File["Local file"] + end + subgraph Server["Registry server"] + direction TB + Config["Configuration"] + Sync["Sync manager"] + Storage["Storage layer"] + APIHandler["API handler"] + end + subgraph Clients["Clients"] + direction LR + ToolHive["ToolHive"] + MCPClient["MCP clients"] + end + Sources -->|sync| Server + Config --> Sync + Sync --> Storage + Storage --> APIHandler + Server -->|REST API| Clients +``` + +## Features + +- **Standards-compliant**: Implements the official MCP Registry API + specification +- **Multiple registry sources**: Git repositories, API endpoints, local files, + managed registries, and Kubernetes discovery +- **Automatic synchronization**: Background sync with configurable intervals and + retry logic for Git, API, and File sources +- **Container-ready**: Designed for deployment in Kubernetes clusters, but can + be deployed anywhere +- **Flexible deployment**: Works standalone or as part of ToolHive + infrastructure +- **Production-ready**: Built-in health checks, graceful shutdown, and sync + status persistence +- **Kubernetes-aware**: Automatic discovery of MCP servers deployed via ToolHive + Operator +- **Skills registry**: Publish and discover reusable + [skills](../concepts/skills.mdx) through the extensions API + +## Registry sources + +The server supports five registry source types: + +1. **Managed Registry** - A fully-managed MCP Registry + - Ideal for private repositories + - Automatically exposes entries following upstream MCP Registry format + - Supports adding new MCP servers via `/publish` endpoint + +2. **Upstream Registry** - Sync from upstream MCP Registry APIs + - Supports federation and aggregation scenarios + - Format conversion from upstream to ToolHive format + - Does not support publishing + +3. **Kubernetes Cluster** - Automatically creates registry entries for running + workloads + - Ideal to quickly grant access to running MCP servers to knowledge workers + - Useful for bigger organizations where MCP server developers differ from + users + - Does not support publishing + +4. **Git Repository** - Clone and sync from Git repositories + - Supports branch, tag, or commit pinning + - Ideal for version-controlled registries + - Does not support publishing + +5. **Local File** - Read from filesystem + - Ideal for local development and testing + - Supports mounted volumes in containers + - Does not support publishing + +## Next steps + +- [Configure registry sources](./configuration.mdx) to set up your data sources, + sync policies, and server filtering +- [Set up authentication](./authentication.mdx) to secure access to your + registry +- [Manage skills](./skills.mdx) to publish and discover reusable skills +- [View the API reference](../reference/registry-api.mdx) for endpoint + documentation diff --git a/versioned_docs/version-1.1/toolhive/guides-registry/skills.mdx b/versioned_docs/version-1.1/toolhive/guides-registry/skills.mdx new file mode 100644 index 00000000..13573af8 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-registry/skills.mdx @@ -0,0 +1,204 @@ +--- +title: Manage skills +description: + How to publish, list, retrieve, and delete skills using the ToolHive Registry + server extensions API. +--- + +The Registry server provides an extensions API for managing +[skills](../concepts/skills.mdx). This guide covers the full lifecycle: +publishing, listing, retrieving, and deleting skills. + +## Prerequisites + +- A running Registry server with at least one **managed** registry configured + (skills can only be published to managed registries) +- `curl` or another HTTP client +- If authentication is enabled, a valid bearer token (see + [Authentication](./authentication.mdx)) + +## API base path + +All skills endpoints use the following base path: + +```text +/{registryName}/v0.1/x/dev.toolhive/skills +``` + +Replace `{registryName}` with the `name` of the registry as defined in your +[configuration file](./configuration.mdx) (for example, `my-registry` if your +config has `registries: [{name: my-registry, ...}]`). + +## Publish a skill + +To publish a new skill version, send a `POST` request with the skill metadata: + +```bash title="Publish a skill" +curl -X POST \ + https://registry.example.com/my-registry/v0.1/x/dev.toolhive/skills \ + -H "Content-Type: application/json" \ + -H "Authorization: Bearer " \ + -d '{ + "namespace": "io.github.acme", + "name": "code-review", + "description": "Performs structured code reviews using best practices", + "version": "1.0.0", + "status": "active", + "title": "Code Review Assistant", + "license": "Apache-2.0", + "packages": [ + { + "registryType": "git", + "url": "https://github.com/acme/skills", + "ref": "v1.0.0", + "subfolder": "code-review" + } + ], + "repository": { + "url": "https://github.com/acme/skills", + "type": "git" + } + }' +``` + +**Required fields:** `namespace`, `name`, `description`, `version` + +A successful response returns `201 Created` with the published skill. If the +version already exists, the server returns `409 Conflict`. + +The `status` field accepts `active`, `deprecated`, or `archived` (case +insensitive). The server normalizes status values to uppercase internally. + +### Versioning behavior + +When you publish a new version, the registry compares it against the current +latest version. If the new version is newer, the latest pointer updates +automatically. Publishing an older version (for example, backfilling `0.9.0` +after `1.0.0` exists) does not change the latest pointer. + +## List skills + +To list skills in a registry: + +```bash title="List all skills" +curl https://registry.example.com/my-registry/v0.1/x/dev.toolhive/skills +``` + +### Query parameters + +| Parameter | Type | Default | Description | +| --------- | ------ | ------- | -------------------------------------------------- | +| `search` | string | - | Filter by name or description substring | +| `status` | string | - | Filter by status (comma-separated, e.g., `active`) | +| `limit` | int | 50 | Maximum results per page (1-100) | +| `cursor` | string | - | Pagination cursor from a previous response | + +### Search example + +```bash title="Search for skills by keyword" +curl "https://registry.example.com/my-registry/v0.1/x/dev.toolhive/skills?search=review&limit=10" +``` + +### Response format + +```json +{ + "skills": [ + { + "namespace": "io.github.acme", + "name": "code-review", + "description": "Performs structured code reviews using best practices", + "version": "1.0.0", + "status": "ACTIVE", + "title": "Code Review Assistant", + "license": "Apache-2.0", + "packages": [ + { + "registryType": "git", + "url": "https://github.com/acme/skills", + "ref": "v1.0.0", + "subfolder": "code-review" + } + ], + "repository": { + "url": "https://github.com/acme/skills", + "type": "git" + } + } + ], + "metadata": { + "count": 1, + "nextCursor": "" + } +} +``` + +Use the `nextCursor` value to fetch the next page of results. An empty +`nextCursor` indicates there are no more pages. + +## Retrieve a skill + +### Get the latest version + +```bash title="Get the latest version of a skill" +curl https://registry.example.com/my-registry/v0.1/x/dev.toolhive/skills/io.github.acme/code-review +``` + +### Get a specific version + +```bash title="Get a specific version" +curl https://registry.example.com/my-registry/v0.1/x/dev.toolhive/skills/io.github.acme/code-review/versions/1.0.0 +``` + +### List all versions + +```bash title="List all versions of a skill" +curl https://registry.example.com/my-registry/v0.1/x/dev.toolhive/skills/io.github.acme/code-review/versions +``` + +## Delete a skill version + +To delete a specific version: + +```bash title="Delete a skill version" +curl -X DELETE \ + https://registry.example.com/my-registry/v0.1/x/dev.toolhive/skills/io.github.acme/code-review/versions/1.0.0 \ + -H "Authorization: Bearer " +``` + +A successful delete returns `204 No Content`. Deleting a non-existent version +returns `404 Not Found`. + +:::warning + +Deleting a skill version is permanent. If the deleted version was the latest, +the latest pointer is not automatically reassigned to a previous version. + +::: + +## Error responses + +The API returns standard HTTP status codes: + +| Code | Meaning | +| ---- | ------------------------------------------------------------- | +| 400 | Invalid request (missing required fields, invalid parameters) | +| 401 | Authentication required | +| 403 | Registry is not a managed registry (read-only registries) | +| 404 | Registry, skill, or version not found | +| 409 | Version already exists | +| 500 | Internal server error | + +## Next steps + +- [Install skills with the CLI](../guides-cli/skills-management.mdx) to install + skills from the registry onto your local machine +- [Configure telemetry](./telemetry-metrics.mdx) to monitor your registry + deployment + +## Related information + +- [Understanding skills](../concepts/skills.mdx) - background on the skills + model +- [Registry server introduction](./intro.mdx) +- [Authentication](./authentication.mdx) - configuring API access diff --git a/versioned_docs/version-1.1/toolhive/guides-registry/telemetry-metrics.mdx b/versioned_docs/version-1.1/toolhive/guides-registry/telemetry-metrics.mdx new file mode 100644 index 00000000..aa3ba82f --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-registry/telemetry-metrics.mdx @@ -0,0 +1,185 @@ +--- +title: Telemetry and metrics +description: + How to enable OpenTelemetry metrics and distributed tracing for the ToolHive + Registry Server +--- + +The ToolHive Registry Server provides comprehensive observability through +OpenTelemetry (OTel), supporting both distributed tracing and metrics collection +via OTLP exporters. This enables you to monitor system behavior, diagnose +issues, and improve performance. + +## Architecture overview + +The Registry Server exports telemetry data (traces and metrics) via OTLP HTTP to +an OpenTelemetry Collector, which can then forward to various backends: + +```mermaid +flowchart TB + subgraph server["Registry Server"] + http["HTTP requests"] + sync["Sync metrics"] + registry["Registry metrics"] + + http & sync & registry --> otlp["OTLP HTTP"] + end + + otlp --> collector["OTel Collector"] + + collector --> tempo["Tempo"] + collector --> prometheus["Prometheus"] + collector --> grafana["Grafana"] +``` + +## Configuration + +Add telemetry configuration to your Registry Server configuration file: + +```yaml title="config.yaml" +telemetry: + enabled: true + serviceName: thv-registry-api + serviceVersion: '1.0.0' + endpoint: otel-collector:4318 + insecure: true + tracing: + enabled: true + sampling: 0.05 + metrics: + enabled: true +``` + +### Configuration options + +| Option | Type | Default | Description | +| ------------------ | ------ | ------------------ | --------------------------------- | +| `enabled` | bool | `false` | Enable or disable all telemetry | +| `serviceName` | string | `thv-registry-api` | Service name in telemetry data | +| `serviceVersion` | string | `"unknown"` | Service version in telemetry data | +| `endpoint` | string | `localhost:4318` | OTLP HTTP endpoint (host:port) | +| `insecure` | bool | `false` | Use insecure connection (no TLS) | +| `tracing.enabled` | bool | `false` | Enable distributed tracing | +| `tracing.sampling` | float | `0.05` | Trace sampling ratio (0.0 to 1.0) | +| `metrics.enabled` | bool | `false` | Enable metrics collection | + +:::note + +The `endpoint` is provided as a hostname and optional port, without a scheme or +path (e.g., use `api.honeycomb.io` or `api.honeycomb.io:443`, not +`https://api.honeycomb.io`). The server automatically uses HTTPS unless +`insecure: true` is specified. + +::: + +## Metrics + +The Registry Server exposes metrics prefixed with `thv_reg_srv_` for easy +identification. + +### Available metrics + +| Metric | Type | Labels | Description | +| ------------------------------------------- | ------------- | -------------------------------- | ------------------------------ | +| `thv_reg_srv_http_request_duration_seconds` | Histogram | `method`, `route`, `status_code` | Duration of HTTP requests | +| `thv_reg_srv_http_requests_total` | Counter | `method`, `route`, `status_code` | Total number of HTTP requests | +| `thv_reg_srv_http_active_requests` | UpDownCounter | - | Number of in-flight requests | +| `thv_reg_srv_servers_total` | Gauge | `registry` | Number of servers per registry | +| `thv_reg_srv_sync_duration_seconds` | Histogram | `registry`, `success` | Duration of sync operations | + +### Histogram buckets + +The metrics use the following histogram bucket boundaries: + +- **HTTP request duration**: 0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1, 2.5, + 5, 10 seconds +- **Sync duration**: 0.1, 0.5, 1, 2.5, 5, 10, 30, 60, 120, 300 seconds + +## Distributed tracing + +The Registry Server implements distributed tracing across two layers: HTTP +requests and service operations. + +### Trace hierarchy + +Traces follow a parent-child hierarchy that shows the complete request flow: + +```text +HTTP Request Span (root) +└── Service Span (child) + └── Database operations with db.system=postgresql +``` + +:::note + +Background sync operations are monitored through metrics (see the +`thv_reg_srv_sync_duration_seconds` metric above) rather than distributed +traces, as they are internal operations without incoming request context. + +::: + +### HTTP layer spans + +All HTTP requests (except health and readiness endpoints) are traced with the +following attributes: + +| Attribute | Type | Description | +| --------------------------- | ------ | -------------------------------------------- | +| `http.request.method` | string | HTTP method (GET, POST, etc.) | +| `http.route` | string | Route pattern (e.g., `/v0.1/servers/{name}`) | +| `url.path` | string | Actual URL path | +| `user_agent.original` | string | Client user agent (truncated to 256 chars) | +| `http.response.status_code` | int | Response status code | + +### Service layer spans + +Database service operations include these attributes: + +| Attribute | Type | Description | +| ----------------------- | ------ | --------------------------------- | +| `registry.name` | string | Name of the registry | +| `server.name` | string | Name of the server | +| `server.version` | string | Version of the server | +| `pagination.limit` | int | Page size limit | +| `pagination.has_cursor` | bool | Whether pagination cursor is used | +| `result.count` | int | Number of results returned | + +### Context propagation + +The Registry Server supports W3C Trace Context propagation. Incoming requests +with `traceparent` headers have their trace context extracted and used as the +parent for all child spans, enabling distributed tracing across multiple +services. + +## Sampling strategies + +Adjust sampling rates based on your environment and traffic volume: + +| Environment | Sampling rate | Use case | +| ----------- | ------------- | -------------------------------------------- | +| Development | `1.0` | Capture all traces for debugging | +| Staging | `0.1` | 10% sampling for testing | +| Production | `0.01 - 0.05` | 1-5% sampling to balance cost and visibility | + +:::tip + +Start with a higher sampling rate and reduce it as you understand your traffic +patterns. For high-traffic production environments, even 1% sampling provides +sufficient data for identifying issues. + +::: + +## Excluded endpoints + +The `/health` and `/readiness` endpoints are intentionally excluded from +tracing. + +These endpoints generate a high volume of nearly identical spans that provide +minimal diagnostic value while significantly increasing storage costs. HTTP +metrics still capture latency and error rates for these endpoints. + +## Next steps + +- [Deploy the server](./deployment.mdx) to your Kubernetes environment +- [Manage skills](./skills.mdx) to publish and discover reusable skills in your + registry diff --git a/versioned_docs/version-1.1/toolhive/guides-ui/cli-access.mdx b/versioned_docs/version-1.1/toolhive/guides-ui/cli-access.mdx new file mode 100644 index 00000000..e7e990a8 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-ui/cli-access.mdx @@ -0,0 +1,147 @@ +--- +title: Access the CLI from ToolHive UI +description: + How to use the ToolHive CLI when using the ToolHive UI application for + advanced features and terminal-based workflows. +--- + +ToolHive UI includes the CLI for advanced users who want terminal access or +features not yet available in the graphical interface. ToolHive UI automatically +installs and manages the CLI, so you don't need to install it separately. + +## Why use the CLI with ToolHive UI? + +While the ToolHive UI covers most common tasks, you might want to use the CLI +for: + +- **Advanced features**: Some features are available in the CLI before they're + added to the graphical interface, such as + [agent skills management](../guides-cli/skills-management.mdx) +- **Scripting and automation**: Integrate MCP server management into local + scripts or automated workflows +- **Personal preference**: If you prefer working in a terminal for certain + tasks, the CLI is available without a separate installation + +## Use CLI commands + +After ToolHive UI installation, you can use the CLI from your terminal: + +1. Open a new terminal window to pick up the PATH changes. + +1. Verify the CLI is available: + + ```bash + thv version + ``` + +1. Run any CLI command: + + ```bash + thv list # List running MCP servers + thv registry list # Browse available servers + thv --help # View all commands + ``` + +For detailed command reference, see the [CLI guides](../guides-cli/index.mdx) +and [command reference](../reference/cli/thv.md). + +## How ToolHive UI manages the CLI + +When you install ToolHive UI, it automatically: + +1. **Creates a symlink** to its bundled CLI binary: + - macOS/Linux: `~/.toolhive/bin/thv` + - Windows: `%LOCALAPPDATA%\ToolHive\bin\thv.exe` + +1. **Configures your PATH** by adding entries to your shell configuration files + (`.bashrc`, `.zshrc`, `config.fish`, or the Windows User PATH) + +This ensures the CLI version always matches the ToolHive UI version, preventing +compatibility issues with the API. + +:::note + +If you have a standalone CLI installed (via Homebrew, WinGet, or manually), it +will show a conflict error. See +[CLI conflict resolution](../guides-cli/install.mdx#cli-conflict-resolution) for +details. + +::: + +## The Settings > CLI page + +ToolHive UI includes a dedicated settings page to manage the CLI installation. +Access it from **Settings** (gear icon) > **CLI**. + +The page displays: + +- CLI installation status and version +- Symlink location and target path +- Shell configuration status + +Use the **Reinstall** button if the CLI becomes unavailable or the symlink +breaks (for example, after moving the ToolHive UI application). + +## Troubleshooting + +
+CLI not found in terminal + +If `thv` is not recognized after installing ToolHive UI: + +1. **Open a new terminal window**: The PATH changes only take effect in new + terminal sessions. + +1. **Check the Settings > CLI page**: Verify that the PATH Configuration shows + "Valid" status. + +1. **Manually source your shell configuration**: + + ```bash + # Bash + source ~/.bashrc + + # Zsh + source ~/.zshrc + + # Fish + source ~/.config/fish/config.fish + ``` + +1. **Reinstall the CLI**: Go to Settings > CLI and click **Reinstall**. + +
+ +
+Broken symlink after moving ToolHive UI + +If you move the ToolHive UI application to a different location, the CLI symlink +may break. To fix this: + +1. Open ToolHive UI from its new location. +1. Go to Settings > CLI. +1. Click **Reinstall** to create a new symlink pointing to the correct location. + +
+ +
+CLI conflict error when running thv + +If you see "CLI conflict detected", you have both ToolHive UI and a standalone +CLI installed. See +[CLI conflict resolution](../guides-cli/install.mdx#cli-conflict-resolution) for +the error message and resolution steps. + +
+ +## Next steps + +- [Run MCP servers](./run-mcp-servers.mdx) from the ToolHive UI +- Explore the full [CLI guides](../guides-cli/index.mdx) for terminal-based + workflows + +## Related information + +- [CLI guides](../guides-cli/index.mdx) +- [CLI command reference](../reference/cli/thv.md) +- [CLI conflict resolution](../guides-cli/install.mdx#cli-conflict-resolution) diff --git a/versioned_docs/version-1.1/toolhive/guides-ui/client-configuration.mdx b/versioned_docs/version-1.1/toolhive/guides-ui/client-configuration.mdx new file mode 100644 index 00000000..2a02c240 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-ui/client-configuration.mdx @@ -0,0 +1,91 @@ +--- +title: Client configuration +description: How to connect ToolHive to AI clients using the UI. +--- + +import ClientIntro from '../_partials/_client-config-intro.mdx'; + + + +## Manage clients + +ToolHive automatically discovers supported AI clients installed on your system. +To choose which clients ToolHive configures, open the **MCP Servers** page and +click **Manage Clients**. This opens a dialog that lists detected clients with +on/off toggles. Discovery is dynamic: if you install a new client, ToolHive +updates the list. + +:::tip[Using groups?] + +If you organize your MCP servers into groups, you can configure client access +per group. This lets you control which servers each client can access. See +[Organize servers into groups](./group-management.mdx) for details. + +::: + +### Connect a client + +To connect a client to ToolHive: + +1. Go to **MCP Servers** and click **Manage Clients**. +2. Turn on the toggle for the client you want to connect. +3. Click **Save** to apply your changes. + +When you connect a client and save, ToolHive configures it to use your running +MCP servers. Any new servers you start are also added automatically. When you +stop or remove an MCP server, ToolHive updates the client's configuration to +remove the server. + +### Disconnect a client + +To disconnect, open **Manage Clients**, turn off the client's toggle, then click +**Save**. ToolHive removes the MCP server configurations from that client. The +client can no longer use ToolHive-managed MCP servers. + +### Save your changes + +Changes you make in **Manage Clients** are not applied until you click **Save**. +If you close the dialog or click **Cancel** without saving, your toggle changes +are discarded. + +## Why don't I see my client? + +If you don't see your client in **Manage Clients**, ToolHive might not support +it, or its configuration file might not be in a location ToolHive recognizes. + +For a list of supported clients and how ToolHive detects them, see the +[client compatibility reference](../reference/client-compatibility.mdx). + +## Why do I still see a client I uninstalled? + +Many clients leave behind configuration files even after you uninstall them. +ToolHive detects these files and continues to show the client in the **Manage +Clients** dialog. If you want to remove the client from ToolHive, delete its +configuration files manually. + +## Manual client configuration + +For clients that ToolHive doesn't support directly, you can still configure them +to connect to ToolHive-managed MCP servers using the SSE (Server-Sent Events) or +Streamable HTTP protocol. + +To do this, you need the URL of the MCP server you want to connect to. You can +get this URL from the **MCP Servers** page in ToolHive. Click the menu (︙) on +the MCP server card to copy the URL. + +You can then configure your client to use this URL. The exact steps depend on +your client or library, so refer to its documentation for details. See the +[client configuration reference](../reference/client-compatibility.mdx#manual-configuration) +for some common examples. + +## Next steps + +- Test your setup in the [Playground](./playground.mdx) +- [Customize which tools](./customize-tools.mdx) each server exposes to your AI + clients + +## Related information + +- [Client compatibility](../reference/client-compatibility.mdx) +- [Run MCP servers](./run-mcp-servers.mdx) +- [Organize servers into groups](./group-management.mdx) diff --git a/versioned_docs/version-1.1/toolhive/guides-ui/customize-tools.mdx b/versioned_docs/version-1.1/toolhive/guides-ui/customize-tools.mdx new file mode 100644 index 00000000..ee66e08f --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-ui/customize-tools.mdx @@ -0,0 +1,207 @@ +--- +title: Customize tools +description: + Select which tools from an MCP server are available to your AI clients using + the ToolHive UI. +--- + +ToolHive lets you customize which tools from a running MCP server are available +to your AI clients. You can enable or disable individual tools, and customize +their names and descriptions. This gives you fine-grained control over the +capabilities exposed by each server, helping you create focused tool sets and +reduce complexity for your AI workflows. + +## Why customize tools? + +Customizing tools helps you: + +- **Reduce complexity**: Hide tools that aren't relevant to your workflow, + making it easier for AI clients to select the right tool +- **Improve performance**: Fewer tools mean faster tool selection and reduced + token usage in AI interactions +- **Enhance security**: Limit exposure to only the tools you need, reducing the + risk of unintended actions +- **Create focused environments**: Tailor tool availability to specific tasks or + projects +- **Clarify tool purpose**: Edit tool names and descriptions to make them more + understandable for your specific use case + +:::info[Registry match required] + +Tool customization is only available for MCP servers that match an entry in the +registry. This includes: + +- Servers installed directly from the registry +- Custom servers with a docker image or URL that matches a registry entry + +Custom servers that don't match any registry entry do not support this feature. + +::: + +## Access tool customization + +To customize tools for a running MCP server: + +1. Open the **MCP Servers** page. +1. Find the server you want to customize and click the menu (︙) on its card. +1. Select **Customize Tools** from the dropdown menu. + +The customize tools page displays all available tools from the MCP server, each +with a toggle switch and description. + +:::note[Server must be running] + +The MCP server must be running to view and customize tools. If the server is +stopped when you access the customize tools page, you'll see a message prompting +you to start the server first. + +::: + +## Enable or disable tools + +On the customize tools page: + +1. Review the list of available tools and their descriptions. +1. Use the toggle switches to enable or disable individual tools: + - **Green (enabled)**: The tool is available to AI clients + - **Gray (disabled)**: The tool is hidden from AI clients +1. Click **Apply** to save your changes. + +The ToolHive proxy filters out disabled tools when AI clients connect to the +server. Clients only see and can only call the tools you've enabled. + +:::warning[At least one tool required] + +You must keep at least one tool enabled. ToolHive prevents you from disabling +all tools on a server, as this would make the server non-functional. + +::: + +## Edit tool names and descriptions + +You can customize the name and description of any tool to make it more suitable +for your workflow. This is useful when the default tool name or description is +unclear, or when you want to adapt the tool's presentation to your specific use +case. + +To edit a tool: + +1. On the customize tools page, find the tool you want to edit. +1. Click the **Edit** button next to the tool. +1. In the dialog that opens: + - Edit the **Tool** field to change the tool's name. + - Edit the **Description** field to customize the tool's description. + - The original values are shown as helper text for reference. + - Leave either field empty to reset it to the original value. +1. Click **Save** to apply your changes, or **Cancel** to discard them. + +Tools with custom names or descriptions are marked with a wrench icon to +indicate they have custom overrides applied. The icon color indicates the +status: + +- **Orange**: The tool has unsaved custom overrides +- **Primary color**: The tool has saved custom overrides + +Your customizations are preserved even if you disable and re-enable the tool. + +:::tip[Unsaved custom overrides] + +If you customize a tool's name or description but haven't clicked **Apply** on +the main page, a tooltip appears indicating "This tool has unsaved custom +overrides". Click **Apply** to save both your tool selection changes and custom +overrides. + +::: + +## How filtering works + +When you customize tools: + +1. You toggle tools on or off using the switches and optionally edit their names + and descriptions. +1. After clicking **Apply**, your settings are saved to the server + configuration. +1. The ToolHive proxy intercepts tool discovery requests from AI clients. +1. Only enabled tools appear in the client's tool list, with your custom names + and descriptions if you've set them. +1. If a client attempts to call a disabled tool directly, the proxy blocks the + request. + +This filtering happens transparently without requiring any changes to your AI +client configuration. Your clients automatically see the updated tool list with +customized names and descriptions the next time they connect. + +:::tip + +If you change tool settings while a client is connected, you may need to restart +the client session to see the updated tool list. Some clients cache tool lists +and don't automatically refresh. + +::: + +## Example workflows + +### Focus on specific GitHub operations + +If you're using the GitHub MCP server but only need pull request tools: + +1. Open **Customize Tools** for the GitHub server. +1. Disable all tools except those related to pull requests (like + `create_pull_request`, `get_pull_request`, `list_pull_requests`). +1. Your AI clients now see only pull request tools, making it easier to work + with GitHub PRs without distraction from issue, branch, or repository tools. + +### Create environment-specific tool sets + +If you're running the same MCP server in different groups for different +environments: + +1. Copy the server to multiple groups (see + [Organize servers into groups](./group-management.mdx)). +1. Customize tools in each group to match the environment's needs. +1. For example, enable only read-only tools in a production group, while + allowing all tools in a development group. + +### Clarify technical tool names + +If an MCP server exposes tools with technical or unclear names: + +1. Open **Customize Tools** for the server. +1. Click **Edit** on tools with unclear names. +1. Change the tool name to something more descriptive (for example, rename + `list_commits_test` to `List Commits`). +1. Update the description to clarify what the tool does in your context. +1. Click **Save**, then **Apply**. +1. Your AI clients now see clearer, more intuitive tool names and descriptions. + +## Reset to defaults + +To restore all tools to their default enabled state and remove custom overrides: + +**To reset an individual tool's name or description:** + +1. Click the **Edit** button next to the tool. +1. Clear the **Tool** and **Description** fields (leave them empty). +1. Click **Save**. +1. Click **Apply** to save your changes. + +The tool name and description revert to their original values, and the wrench +icon is removed. + +**To enable all tools:** + +1. Open the customize tools page for the server. +1. Manually enable all tools using the toggle switches. +1. Click **Apply** to save your changes. + +## Next steps + +- Try out your customized tools in the [Playground](./playground.mdx) +- [Configure client access](./client-configuration.mdx) to connect your AI + clients to ToolHive + +## Related information + +- [Run MCP servers](./run-mcp-servers.mdx) +- [Organize servers into groups](./group-management.mdx) +- [Client configuration](./client-configuration.mdx) diff --git a/versioned_docs/version-1.1/toolhive/guides-ui/group-management.mdx b/versioned_docs/version-1.1/toolhive/guides-ui/group-management.mdx new file mode 100644 index 00000000..2b1aff84 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-ui/group-management.mdx @@ -0,0 +1,166 @@ +--- +title: Organize servers into groups +description: + How to organize MCP servers into groups and configure client access in the + ToolHive UI. +--- + +This guide explains how to organize your MCP servers into groups and configure +which groups your MCP clients can access. + +:::tip[New to groups?] + +If you're not sure whether groups are right for your use case, see +[Organizing MCP servers with groups](../concepts/groups.mdx) for an overview of +when and why to use them. + +::: + +:::info[What's the default behavior?] + +The `default` group always exists and cannot be deleted. When you add an MCP +server, the `default` group is preselected in the group field unless you choose +a different group. + +::: + +## View and navigate groups + +The **MCP Servers** page includes a sidebar on the left that lists all your +groups. The active group is highlighted. + +To view servers in a different group, click on the group name in the sidebar. +The **MCP Servers** page updates to show only the servers in that group. + +## Create a group + +To create a new group: + +1. Open the **MCP Servers** page. +2. Click **Add a group** in the sidebar. +3. Enter a unique name for the group. +4. Click **Create**. + +:::note + +Group names must be unique. If you try to create a group with a name that +already exists, you'll see an error message. + +::: + +## Assign servers to groups + +When you install or configure an MCP server, you assign it to a group. All +server types (registry, custom local, and remote) include a **Group** field in +their configuration forms. + +### Assign a server during installation + +When installing a new MCP server from the registry or adding a custom server: + +1. Fill in the server configuration. +2. In the **Group** field, select the group where you want to add the server. +3. Click **Install server**. + +### Copy a server to a different group + +You can copy a server to a different group: + +1. On the **MCP Servers** page, find the server you want to copy. +2. Click the menu (︙) on the server card. +3. Select **Copy server to a group**. +4. Choose the destination group. +5. Click **Copy**. + +This creates a complete copy of the server in the destination group, preserving +all configuration including secrets, environment variables, and storage volumes. +The original server remains in its current group. + +## Manage client access per group + +You can control which AI clients have access to servers in each group. This lets +you configure different tool sets for different clients or environments. + +To manage client access for a group: + +1. Navigate to the group by clicking its name in the sidebar. +2. Click **Manage Clients**. +3. Toggle the switches to enable or disable clients for this group. +4. Click **Save**. + +When you enable a client for a group, ToolHive registers that client to access +all servers in the group. When you disable a client for a group, ToolHive +unregisters the client from that group. + +:::tip + +You can enable the same client for multiple groups. The client will have access +to servers from all groups where it's enabled. + +::: + +## Delete a group + +To delete a group: + +1. Navigate to the group by clicking its name in the sidebar. +2. Click the group actions menu (⋮) in the header. +3. Select **Delete group**. +4. Confirm the deletion in the dialog. + +:::warning + +Deleting a group permanently removes all servers in that group. This action +cannot be undone. If you want to preserve the servers, copy them to another +group before deleting. + +::: + +You cannot delete the `default` group. + +## Example workflows + +### Separate development and production servers + +1. Create two groups: "development" and "production". +2. Install your MCP servers, assigning development tools to the "development" + group and production tools to the "production" group. +3. Configure your primary AI client (like GitHub Copilot) to access only the + "development" group for day-to-day coding work. +4. Use a different client or manually configure clients to access the + "production" group when needed. + +### Create environment-specific tool sets + +If you're running the same MCP server in different groups for different +environments: + +1. Copy the server to multiple groups (see + [Copy a server to a different group](#copy-a-server-to-a-different-group)). +2. Customize tools in each group to match the environment's needs (see + [Customize tools](./customize-tools.mdx)). +3. For example, enable only read-only tools in a production group, while + allowing all tools in a development group. + +### Project-based organization + +1. Create groups for each project: "webapp-frontend", "webapp-backend", and + "mobile-app". +2. Install project-specific MCP servers in each group (for example, React tools + in "webapp-frontend", database tools in "webapp-backend"). +3. Configure different AI clients or workspaces to access the appropriate groups + for each project. + +## Next steps + +- [Manage secrets](./secrets-management.mdx) for the MCP servers in your groups +- [Customize which tools](./customize-tools.mdx) each server exposes to your AI + clients +- [Configure client access](./client-configuration.mdx) to connect your AI + clients to ToolHive + +## Related information + +- [Run MCP servers](./run-mcp-servers.mdx) +- [Client configuration](./client-configuration.mdx) +- [Secrets management](./secrets-management.mdx) diff --git a/versioned_docs/version-1.1/toolhive/guides-ui/index.mdx b/versioned_docs/version-1.1/toolhive/guides-ui/index.mdx new file mode 100644 index 00000000..5c21e16e --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-ui/index.mdx @@ -0,0 +1,57 @@ +--- +title: Using ToolHive +description: How-to guides for using the ToolHive UI to run and manage MCP servers. +--- + +import DocCardList from '@theme/DocCardList'; + +import useBaseUrl from '@docusaurus/useBaseUrl'; +import ThemedImage from '@theme/ThemedImage'; + +## Introduction + +The ToolHive UI is a desktop application that makes it easy to run and manage +Model Context Protocol (MCP) servers on your local machine. It provides a +user-friendly interface to discover, deploy, and manage MCP servers with +security and ease of use built in. + +It's designed for anyone who wants to run MCP servers without needing to +understand the complexities of Docker or command-line tools. Whether you're a +developer, researcher, or just curious about MCP servers, ToolHive provides a +simple way to get started. + + +
+ +We strive to make ToolHive intuitive and easy to use. If we've missed the mark +on something, [let us know](https://discord.gg/stacklok)! + +:::tip[Advanced users] + +ToolHive UI includes and manages the CLI automatically for terminal access and +advanced features. See [Access the CLI from ToolHive UI](./cli-access.mdx) for +details. + +::: + +## Where to start + +- **New to ToolHive?** Follow the [Quickstart](./quickstart.mdx) to install the + app and run your first MCP server in minutes. +- **Already installed?** Jump straight to + [Run MCP servers](./run-mcp-servers.mdx) to discover and launch servers from + the registry. +- **Want to connect your AI client?** See + [Client configuration](./client-configuration.mdx) to point Claude, Cursor, or + another client at your running servers. + +## Contents + + diff --git a/versioned_docs/version-1.1/toolhive/guides-ui/install.mdx b/versioned_docs/version-1.1/toolhive/guides-ui/install.mdx new file mode 100644 index 00000000..5fdaa1e7 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-ui/install.mdx @@ -0,0 +1,269 @@ +--- +title: Install ToolHive +description: How to install the ToolHive UI application. +--- + +This guide walks you through installing, upgrading, and managing the ToolHive +desktop application. + +![Latest version](https://img.shields.io/github/v/release/stacklok/toolhive-studio?style=for-the-badge&label=Latest%20version&color=bddfc2) +![Release date](https://img.shields.io/github/release-date/stacklok/toolhive-studio?display_date=published_at&style=for-the-badge&label=Released&color=bddfc2) + +## Prerequisites + +Before installing ToolHive, make sure your system meets these requirements: + +- **Operating systems**: + - macOS (Apple silicon or Intel) + - Windows 10/11 (x64) + - Linux (x86_64/amd64) +- **Container runtime**: + - Docker / Docker Desktop + - Podman / Podman Desktop + - Colima with Docker runtime + - Rancher Desktop with the dockerd/moby runtime (experimental) + +ToolHive requires minimal CPU, memory, and disk space. The exact requirements +depend on how many MCP servers you run and the resources they use. + +## Install ToolHive + +Select your operating system to see the installation instructions. + + + + +Download the latest ToolHive installer for +[Apple silicon](https://github.com/stacklok/toolhive-studio/releases/latest/download/ToolHive-arm64.dmg) +or +[Intel-based](https://github.com/stacklok/toolhive-studio/releases/latest/download/ToolHive-x64.dmg) +Macs and open the DMG file. + +Copy the ToolHive app to your Applications folder. You can then open it from +your Applications folder, Launchpad, or using Spotlight search. + + + + +Download the latest +[ToolHive installer](https://github.com/stacklok/toolhive-studio/releases/latest/download/ToolHive.Setup.exe) +and run the setup executable. + +After installation, you can find ToolHive in your Start menu or on your desktop. + +:::info[Important] + +The first time you run ToolHive, you may be prompted to allow firewall access. +If you don't allow this, ToolHive won't be able to run MCP servers. + +::: + + + + +1. Download the appropriate RPM or DEB package for your distribution from the + [ToolHive UI releases page](https://github.com/stacklok/toolhive-studio/releases/latest) + +2. Use your package manager to install the downloaded package: + - For RPM-based distributions (like Fedora or Red Hat Enterprise Linux): + + ```bash + sudo rpm -i ToolHive--1.x86_64.rpm + ``` + + - For DEB-based distributions (like Ubuntu or Debian): + + ```bash + sudo dpkg -i toolhive__amd64.deb + ``` + +For other Linux distributions, download the +[binary tarball](https://github.com/stacklok/toolhive-studio/releases/latest/download/toolhive-studio-linux-x64.tar.gz) +and extract it, then run the `ToolHive` binary directly. + + + + +## System tray icon + +When you close the ToolHive application window, it continues running in the +background so your MCP servers remain available. ToolHive installs a system tray +icon for quick access. You can use it to: + +- Enable or disable **Start on login** +- Show or hide the ToolHive application window +- Quit ToolHive, which stops all running MCP servers + +## Application settings + +Open the ToolHive settings screen from the gear icon (⚙️) in the application +window. The settings screen allows you to configure various options: + +- **Display theme**: Choose between light and dark themes for the application. + ToolHive matches your system theme by default. +- **Start on login**: Automatically start ToolHive when you log in to your + system. MCP servers that were running when you quit ToolHive are restarted + automatically. +- **Error reporting**: Enable or disable error reporting and telemetry data + collection. +- **Skip quit confirmation**: Skip the MCP server shutdown confirmation dialog + when quitting ToolHive. + +From the settings screen, you can also view version information and download the +application log file for troubleshooting. + +## Upgrade ToolHive + +ToolHive automatically checks for updates. When a new version is available, +you'll see a notification in the application. During the upgrade, ToolHive stops +all running MCP servers, updates the application, and then restarts itself and +the MCP servers. + +You can also manually install updates by downloading the latest installer for +your operating system from the +[ToolHive UI releases page](https://github.com/stacklok/toolhive-studio/releases/latest) +and running it. The installer will upgrade your existing ToolHive installation +to the latest version. See the [Install ToolHive](#install-toolhive) section for +direct download links. + +## File locations + +ToolHive stores its configuration and data files in several locations depending +on your operating system: + + + + +- The `~/Library/Application Support/ToolHive` directory contains: + - Configuration files and application data for the ToolHive UI + - MCP server logs and configurations (`logs/` and `runconfigs/` directories) + - Your encrypted secrets store (`secrets_encrypted` file) + - ToolHive CLI/API configuration file (`config.yaml`) +- The main UI application log is located at `~/Library/Logs/ToolHive/main.log` + +Since macOS is not case sensitive, the `~/Library/Application Support/ToolHive` +directory is shared by the UI and CLI if you have both installed. + + + + +- The `%LOCALAPPDATA%\ToolHive` directory contains: + - Application executables and installation logs + - MCP server logs and configurations (`logs/` and `runconfigs/` directories) + - Your encrypted secrets store (`secrets_encrypted` file) + - ToolHive CLI/API configuration file (`config.yaml`) +- The `%APPDATA%\ToolHive` directory contains: + - Configuration files and application data for the ToolHive UI +- The main UI application log is located at `%APPDATA%\ToolHive\logs\main.log` + +Since Windows is not case sensitive, the `%LOCALAPPDATA%\ToolHive` directory is +shared by the UI and CLI if you have both installed. + + + + +- The `~/.config/ToolHive` directory contains: + - Configuration files and application data for the ToolHive UI +- The `~/.config/toolhive` directory contains (note the case sensitivity): + - MCP server logs and configurations (`logs/` and `runconfigs/` directories) + - Your encrypted secrets store (`secrets_encrypted` file) + - ToolHive CLI/API configuration file (`config.yaml`) +- The main UI application log is located at `~/.config/ToolHive/logs/main.log` + +Since Linux is case sensitive, the `~/.config/ToolHive` and `~/.config/toolhive` +directories are separate. However, the ToolHive UI and CLI share the same +configuration file and secrets store to support coexistence. + + + + +You can also download the application log file from the **Settings** screen (⚙️) +in the ToolHive UI. + +## Telemetry and error reporting + +ToolHive uses [Sentry](https://sentry.io/welcome/) for error tracking and +performance monitoring to help us identify and fix issues, improve stability, +and enhance the user experience. This telemetry is enabled by default. You can +disable this by turning off the **Error reporting** option in the settings +screen (⚙️) if you prefer not to share this data. + +ToolHive collects the following information: + +- Error reports and crash logs +- Performance metrics +- Usage patterns + +This data is anonymized and does not include any personally identifiable +information. It helps us understand how ToolHive is used and identify areas for +improvement. Review the +[Stacklok privacy policy](https://www.iubenda.com/privacy-policy/29074746) and +[Terms of Service](https://stacklok.com/terms-of-service) for more details. + +## Next steps + +Now that you have ToolHive installed, you can start using it to run and manage +MCP servers. See [Run MCP servers](./run-mcp-servers.mdx) to get started. + +:::tip[CLI access for advanced users] + +ToolHive UI includes the CLI for terminal access and advanced features. See +[Access the CLI from ToolHive UI](./cli-access.mdx) to learn more. + +::: + +## Related information + +- Quickstart: [Getting started with the ToolHive UI](./quickstart.mdx) +- [Client configuration](./client-configuration.mdx) +- [Secrets management](./secrets-management.mdx) + +## Troubleshooting + +
+Connection Refused error on startup + +If you see a "Connection Refused" error when starting ToolHive, your container +runtime (Docker, Podman, or Colima) is likely not installed, not running, or not +configured correctly. + +Follow the instructions in the error message to install or start your container +runtime. For example, if you're using Docker Desktop, make sure it's running and +that the Docker daemon is active. + +If the retry button doesn't work, restart ToolHive. + +
+ +
+No system tray icon on Linux + +Recent versions of Fedora Linux and other distributions have removed the +AppIndicator extension from their default installations. ToolHive requires this +extension for the system tray icon to work properly. + +On Fedora, install the `gnome-shell-extension-appindicator` package: + +```bash +sudo dnf install gnome-shell-extension-appindicator +``` + +You'll need to log out and log back in to activate the extension. + +Alternatively, install the +[Extension Manager](https://github.com/mjakeman/extension-manager) app. It's +available as a native package in many distributions, or you can install it from +[Flathub](https://flathub.org/apps/com.mattjakeman.ExtensionManager). Then, use +Extension Manager to install the +[AppIndicator](https://extensions.gnome.org/extension/615/appindicator-support/) +extension (listed as "AppIndicator and KStatusNotifierItem Support"). + +The ToolHive icon should now appear in your system tray. + +
+ +### Other issues + +For other installation issues, check the +[GitHub issues page](https://github.com/stacklok/toolhive-studio/issues) or join +the [Discord community](https://discord.gg/stacklok). diff --git a/versioned_docs/version-1.1/toolhive/guides-ui/mcp-optimizer.mdx b/versioned_docs/version-1.1/toolhive/guides-ui/mcp-optimizer.mdx new file mode 100644 index 00000000..c80a6bc4 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-ui/mcp-optimizer.mdx @@ -0,0 +1,127 @@ +--- +title: Optimize MCP tool usage +description: Enable the MCP Optimizer to enhance tool selection and reduce token usage. +--- + +## Overview + +The ToolHive MCP Optimizer acts as an intelligent intermediary between AI +clients and MCP servers. It provides tool discovery, unified access to multiple +MCP servers through a single endpoint, and intelligent routing of requests to +appropriate MCP tools. + +:::info[Status] + +The MCP Optimizer is currently experimental. If you try it out, please share +your feedback on the [Stacklok Discord community](https://discord.gg/stacklok). + +::: + +Learn more about the MCP Optimizer, its benefits, and how it works in the +tutorial: +[Reduce token usage with MCP Optimizer](../tutorials/mcp-optimizer.mdx). + +## Enable MCP Optimizer + +:::note[Limitations] + +MCP Optimizer does not currently work on Linux hosts or with Podman Desktop on +Windows. Supported configurations: + +- macOS with Docker Desktop, Podman Desktop, or Rancher Desktop +- Windows with Docker Desktop or Rancher Desktop + +MCP Optimizer also does not currently work with MCP servers that have network +isolation enabled. + +::: + +To enable the MCP Optimizer in the ToolHive UI: + +1. Open the **Settings** (⚙️) screen and enable **MCP Optimizer** under + **Experimental Features** +2. Click the **Configure** button on the notification that pops up, or go to the + main **MCP Servers** screen and click **MCP Optimizer** in the left sidebar +3. Select the group that contains the MCP servers you want to optimize and click + **Apply Changes** + +ToolHive automatically updates clients that were registered with the selected +group to use the MCP Optimizer. If you want to connect a new client, go to the +group which is enabled for optimization and use the **Manage Clients** button to +register it. + +For best results, connect your client to only the optimized group. If you +connect it to multiple groups, ensure there is no overlap in MCP servers between +the groups to avoid unpredictable behavior. + +:::info[What's happening?] + +When you enable MCP Optimizer, ToolHive automatically creates an internal group +and runs the `mcp-optimizer` MCP server in that group. + +The MCP Optimizer server discovers the MCP servers in the selected group and +builds an index of their tools for intelligent routing. Automatic polling keeps +the index up to date as servers are added or removed from the optimized group. + +ToolHive also disconnects your AI clients from the original MCP server group and +reconnects them to the MCP Optimizer group. + +::: + +## Customize MCP Optimizer settings + +To update the MCP Optimizer's default settings, click the **Advanced** menu and +select **Customize MCP Optimizer configuration**. This opens a form where you +can modify the `mcp-optimizer` MCP server's configuration. + +:::note + +Changes to the MCP Optimizer configuration might cause the feature to stop +working correctly. Only modify these settings if you understand their +implications. + +Generally, you should only need to change the Docker image tag and the +environment variables detailed below. + +::: + +You can customize the MCP Optimizer's behavior using the following environment +variables: + +- `MAX_TOOLS_TO_RETURN`: Number of tools to return from `find_tool` (default: + `8`) +- `TOOL_DISTANCE_THRESHOLD`: Distance threshold for tool similarity (default: + `1.0`) +- `MAX_TOOL_RESPONSE_TOKENS`: Maximum number of tokens to return from + `call_tool` (default: `None`) +- `WORKLOAD_POLLING_INTERVAL`: Polling interval for running MCP servers, in + seconds (default: `60`) +- `REGISTRY_POLLING_INTERVAL`: Polling interval for ToolHive registry, in + seconds (default: `86400`, 24 hours) + +## Disable MCP Optimizer + +To disable the MCP Optimizer, open the **Settings** (⚙️) screen and disable the +**MCP Optimizer** option under the **Experimental Features** section. + +ToolHive stops and removes the MCP Optimizer server and reconnects your clients +to the original MCP server group. + +## Troubleshooting + +If you encounter issues while using the MCP Optimizer, check the logs for +debugging information. On the **MCP Optimizer** page, click the **Advanced** +menu and select **MCP Optimizer logs**. + +## Next steps + +- Try the [Playground](./playground.mdx) to test your optimized tool set +- [Organize MCP servers into groups](./group-management.mdx) to control which + servers the optimizer includes + +## Related information + +- Tutorial: + [Reduce token usage with MCP Optimizer](../tutorials/mcp-optimizer.mdx) +- [Optimizing LLM context](../concepts/tool-optimization.mdx) - background on + tool filtering and context pollution diff --git a/versioned_docs/version-1.1/toolhive/guides-ui/network-isolation.mdx b/versioned_docs/version-1.1/toolhive/guides-ui/network-isolation.mdx new file mode 100644 index 00000000..842b58ca --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-ui/network-isolation.mdx @@ -0,0 +1,108 @@ +--- +title: Network isolation +description: How to configure network isolation for MCP servers in ToolHive. +--- + +import useBaseUrl from '@docusaurus/useBaseUrl'; +import ThemedImage from '@theme/ThemedImage'; + +Most MCP servers require network access to function properly—for example, to +access APIs, download data, or communicate with other services. However, +malicious or misconfigured servers can also exfiltrate sensitive data or +download unwanted content. + +When you install an MCP server in ToolHive, you can optionally enable _network +isolation_. This feature restricts the MCP server's network access to only the +resources you specify. + +:::note + +Network isolation currently supports HTTP and HTTPS connections only. Other +protocols are not supported. + +::: + +## Enabling network isolation + +Network isolation is available for local MCP servers installed from the registry +or custom servers. It is not available for remote MCP servers, which are hosted +and outside of ToolHive. + +During the MCP server installation, select the **Network isolation** tab in the +configuration form. Click the toggle to enable it. + +When you enable network isolation, any safe default configuration defined in the +registry is pre-loaded in the form. You can accept these defaults or customize +the settings to specify which hosts and ports the MCP server is allowed to +access: + +- **Allowed hosts**:\ + A list of hostnames or IP addresses that the MCP server is allowed to access. + This can include APIs, data sources, or other services that the MCP server + needs to function properly. + + :::tip + + To allow access to all subdomains under a specific domain, add a leading + period (`.`) in front of the hostname. For example, to allow access to all + subdomains of `github.com`, enter `.github.com` in the allowed hosts list. + + ::: + +- **Allowed ports**:\ + A list of ports that the MCP server is allowed to use for outgoing + connections. This can help prevent the MCP server from accessing unauthorized + services or resources. For example, port 443 is the default port for HTTPS + connections. + +:::info[Important] + +If you do not specify any allowed hosts or ports, the MCP server will not be +able to access any external resources, including the internet. This can be +useful for MCP servers that do not require network access or for testing +purposes. + +::: + +## Example configuration + +The configuration pictured below allows the MCP server to access +`api.github.com` and all subdomains of `githubusercontent.com` on port 443 +(HTTPS): + + +
+ +### Accessing other workloads on the same container network + +To allow an MCP server to access other workloads on the same network, you need +to configure network isolation to include the appropriate hostnames and ports. +This is commonly needed when your MCP server needs to communicate with +databases, APIs, or other services that are running on your local host during +development. + +For example, in Docker environments, you can add `host.docker.internal` to +access services on the host. `host.docker.internal` is a special hostname +provided by Docker that resolves to the host machine's IP address from within +containers. + +- **Allowed hosts**: `host.docker.internal` +- **Allowed ports**: `3000` + +## Next steps + +- [Customize which tools](./customize-tools.mdx) each server exposes to your AI + clients +- [Configure client access](./client-configuration.mdx) to connect your AI + clients to ToolHive + +## Related information + +- [Run MCP servers](./run-mcp-servers.mdx) diff --git a/versioned_docs/version-1.1/toolhive/guides-ui/playground.mdx b/versioned_docs/version-1.1/toolhive/guides-ui/playground.mdx new file mode 100644 index 00000000..fe681cce --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-ui/playground.mdx @@ -0,0 +1,266 @@ +--- +title: Test MCP servers +description: + Use the playground feature to test and validate MCP servers directly in the + ToolHive UI with AI model providers. +--- + +import useBaseUrl from '@docusaurus/useBaseUrl'; +import ThemedImage from '@theme/ThemedImage'; + +ToolHive's playground lets you test and validate MCP servers directly in the UI +without requiring additional client setup. This streamlined testing environment +helps you quickly evaluate functionality and behavior before deploying MCP +servers to production environments. + +## Key capabilities + +### Instant testing of MCP servers + +Configure your AI model providers, select your MCP servers and tools, and begin +testing immediately in the desktop app. The playground eliminates the friction +of setting up external AI clients just to validate that your MCP servers work +correctly. + +### Detailed interaction logs + +See tool details, parameters, and execution results directly in the UI, ensuring +full visibility into tool performance and responses. Every interaction is +logged, making it easy to understand exactly what your MCP servers are doing and +how they respond to requests. + +### Integrated ToolHive management + +The playground includes a built-in MCP server that lets you manage your other +MCP servers directly through natural language commands. You can list servers, +check their status, start or stop them, and perform other management tasks using +conversational AI. + +## Getting started + +To start using the playground: + +1. **Access the playground**: Click the **Playground** tab in the ToolHive UI + navigation bar. + +2. **Configure provider settings**: Click **Provider Settings** to set up access + to AI model providers: + - **OpenAI**: Enter your OpenAI API key to use GPT models + - **Anthropic**: Add your Anthropic API key for Claude models + - **Google**: Configure Google AI API key for Gemini models + - **xAI**: Set up xAI API key for Grok models + - **Ollama**: Enter the server URL to connect to your local Ollama instance + (default: `http://localhost:11434`) + - **LM Studio**: Enter the server URL from the **Developer** section in LM + Studio where you started the local server (default: + `http://localhost:1234`) + - **OpenRouter**: Add OpenRouter API key for access to multiple model + providers + +3. **Select MCP tools**: Click the tools icon to manage which MCP servers and + tools are available in the playground. + - View all your running MCP servers + - Enable or disable specific tools from each server + - Search and filter tools by name or functionality + - The `toolhive mcp` server is included by default, providing management + capabilities + + + + :::tip + + For more control over tool availability, use + [Customize tools](./customize-tools.mdx) to permanently configure which tools + are enabled for each registry server. The playground tool selection is + temporary and only affects your testing session. + + ::: + +4. **Start testing**: Begin chatting with your chosen AI model. The model will + have access to all enabled MCP tools and can execute them based on your + requests. + +## Using the playground + +### Testing MCP server functionality + +Use the playground to validate that your MCP servers work as expected: + +```text +Can you list all my MCP servers and show their current status? +``` + +The AI will use the `list_servers` tool from the ToolHive MCP server to provide +a comprehensive overview of your server status. + + + +Or test that an individual MCP tool is working as expected: + +```text +Use the GitHub MCP server to search for recent issues in the microsoft/vscode repository +``` + +If you have the GitHub MCP server running, the AI will execute the appropriate +GitHub API calls and return formatted results. + +### Managing servers through conversation + +The ToolHive desktop app automatically starts a dedicated MCP server +(`toolhive mcp`) that orchestrates ToolHive operations through natural language +commands. This approach provides several key benefits: + +- **Unified interface**: Manage your MCP infrastructure using the same + conversational AI interface you use for testing +- **Contextual operations**: The AI understands your current server state and + can make intelligent decisions about which servers to start, stop, or + troubleshoot +- **Reduced complexity**: No need to switch between the chat interface and + traditional UI controls—everything can be done through conversation +- **Audit trail**: All management operations are logged in the same transparent + way as tool executions, providing clear visibility into what actions were + taken + +Take advantage of these integrated ToolHive management tools: + +```text +Start the fetch MCP server for me +``` + +```text +Stop all unhealthy MCP servers +``` + +```text +Show me the logs for the meta-mcp server +``` + +### Validating tool responses + +The playground shows detailed information about each tool execution: + +- **Tool name and description**: What tool was called and its purpose +- **Input parameters**: The exact parameters passed to the tool +- **Execution status**: Whether the tool succeeded or failed +- **Response data**: The complete response from the tool +- **Timing information**: How long the tool took to execute + +This visibility helps you understand exactly how your MCP servers are behaving +and identify any issues with tool implementation or configuration. + +## Recommended practices + +### Provider security + +- Use dedicated API keys for testing that have appropriate rate limits +- Regularly rotate API keys used in development environments +- Consider using API keys with restricted permissions for testing purposes +- When using local providers like Ollama or LM Studio, ensure the server URLs + are only accessible on your local network to prevent unauthorized access + +### Server management + +- Start only the MCP servers you need for testing to improve performance +- Use the playground to validate new server configurations before connecting + them to production AI clients +- Test different combinations of tools to understand how they work together + +### Testing workflow + +1. **Isolated testing**: Test individual MCP servers one at a time to validate + their functionality +2. **Integration testing**: Enable multiple servers to test how they work + together +3. **Performance validation**: Monitor tool execution times and responses under + different loads +4. **Error handling**: Intentionally trigger error conditions to validate proper + error handling + +## Troubleshooting + +### Common issues + +
+Provider not working + +If your provider isn't working: + +1. **For API key-based providers** (OpenAI, Anthropic, Google, xAI, OpenRouter): + - Verify the API key is correct and has proper permissions + - Check that you have sufficient quota or credits with the provider + - Ensure the API key hasn't expired or been revoked + - Try creating a new API key from the provider's dashboard + +2. **For local providers** (Ollama, LM Studio): + - Verify the server is running and accessible at the configured URL + - Check that the server URL is correct and includes the port number + - Ensure no firewall or network settings are blocking the connection + - For LM Studio, confirm you started the server in the **Developer** section + +
+ +
+MCP tools not appearing + +If your MCP server tools aren't showing up: + +1. Verify the MCP server is running (check the **MCP Servers** page) +2. Click the tools icon and ensure the server's tools are enabled +3. Try restarting the MCP server if it shows as unhealthy +4. Check the server logs for any error messages + +
+ +
+Tool execution failing + +If tools are failing to execute: + +1. Check the tool's parameter requirements in the audit log +2. Verify any required secrets or environment variables are configured +3. Ensure the MCP server has necessary permissions (network access, file system + access) +4. Review the server logs for detailed error information + +
+ +## Next steps + +- Learn about [client configuration](./client-configuration.mdx) to connect + ToolHive to external AI applications +- Set up [secrets management](./secrets-management.mdx) for secure handling of + API keys and tokens +- Explore [network isolation](./network-isolation.mdx) for enhanced security + when testing untrusted MCP servers +- Browse the [registry](./registry.mdx) to discover new MCP servers to test in + the playground + +## Related information + +- [Run MCP servers](./run-mcp-servers.mdx) +- [Install ToolHive](./install.mdx) diff --git a/versioned_docs/version-1.1/toolhive/guides-ui/quickstart.mdx b/versioned_docs/version-1.1/toolhive/guides-ui/quickstart.mdx new file mode 100644 index 00000000..62e04e13 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-ui/quickstart.mdx @@ -0,0 +1,171 @@ +--- +title: 'Quickstart: ToolHive UI' +sidebar_label: Quickstart +description: + A step-by-step guide to installing the ToolHive desktop application and + running your first MCP server. +--- + +In this tutorial, you'll install the ToolHive desktop application and run your +first MCP server. By the end, you'll have a working MCP server that can fetch +content from websites and be used by AI applications like GitHub Copilot or +Cursor. + +## What you'll learn + +- How to install ToolHive on your system +- How to find available MCP servers +- How to run an MCP server +- How to use the server with an AI client application + +## Prerequisites + +Before starting this tutorial, make sure you have: + +- [Docker](https://docs.docker.com/get-docker/) or + [Podman](https://podman-desktop.io/downloads) or + [Colima](https://github.com/abiosoft/colima) installed and running +- A [supported MCP client](../reference/client-compatibility.mdx) like GitHub + Copilot in VS Code, Cursor, Claude Code, and more + +## Step 1: Install the ToolHive UI + +First, download and run the ToolHive installer for your system. + +- **macOS**: download the DMG installer for + [Apple silicon](https://github.com/stacklok/toolhive-studio/releases/latest/download/ToolHive-arm64.dmg) + or + [Intel-based](https://github.com/stacklok/toolhive-studio/releases/latest/download/ToolHive-x64.dmg) + Macs and copy the ToolHive application to your Applications folder +- **Windows**: download and run the + [installer](https://github.com/stacklok/toolhive-studio/releases/latest/download/ToolHive.Setup.exe) + +- **Linux**: download the RPM or DEB package from the + [releases page](https://github.com/stacklok/toolhive-studio/releases/latest) + and install it using your package manager. + +For more detailed installation instructions, see the +[installing ToolHive](../guides-ui/install.mdx) guide. + +## Step 2: Find an MCP server to run + +On the initial splash screen, click **Browse registry**, or open the +**Registry** page from the top menu bar. This page lists the MCP servers in +ToolHive's built-in registry. + +:::info[What is this?] + +ToolHive maintains a curated registry of MCP servers that have been verified to +work correctly. The registry includes information about what each server does +and how to use it. + +::: + +For this tutorial, you'll use the "fetch" server, which is a simple MCP server +that lets AI agents get the contents of a website. Click on the "fetch" server +to start the installation process. + +## Step 3: Install the Fetch MCP server + +The Fetch MCP server doesn't require any special configuration, so you can +install it with the default settings. Click the **Install server** button to +start the installation. + +Once the server is installed, it will appear on the **MCP Servers** page. + +:::info[What's happening?] + +ToolHive downloads the container image for the fetch server, creates a container +with the appropriate security settings, and starts the server. It also sets up a +proxy process that lets your AI agent communicate with the server. + +::: + +## Step 4: Connect an AI client + +On the **Clients** page, you'll see the supported AI clients that ToolHive can +manage for you. When you connect a client, ToolHive configures it to use the MCP +servers you have installed. + +Click the toggle switch to connect the client you want to use. + +:::info[What's happening?] + +When you connect a supported client, ToolHive automatically configures it to use +MCP servers that you install. This means you don't have to manually configure +the client each time you run an MCP server. + +::: + +## Step 5: Use the MCP server with your client + +Now that your MCP server is running and your client is connected to ToolHive, +you can use the MCP server's tools. Open your client and ask the AI to fetch +content from a website. Note that you might need to restart your client for the +changes to take effect. + +For example, try asking: "Can you fetch the content from https://toolhive.dev +and summarize it for me?" + +The AI should be able to use the Fetch MCP server to retrieve the content and +provide a summary. + +:::info[What's happening?] + +When you ask the AI agent to fetch content, the large language model (LLM) +determines that it needs external data. It discovers the fetch tool provided by +your MCP server, instructs the agent to execute the tool with the URL you +specified, then receives and processes the webpage content to create a summary. + +::: + +## What's next? + +Congratulations! You've successfully installed ToolHive and run your first MCP +server. Here are some next steps to explore: + +- Learn more about MCP concepts in the + [MCP primer guide](../concepts/mcp-primer.mdx) +- Try [running more MCP servers](../guides-ui/run-mcp-servers.mdx) from the + registry or from custom sources +- Learn about [secrets management](../guides-ui/secrets-management.mdx) for MCP + servers that require authentication +- Learn how to + [manually configure clients](../reference/client-compatibility.mdx#manual-configuration) + that ToolHive doesn't automatically configure + +:::tip[Ready to go further?] + +Stacklok Enterprise extends ToolHive with centralized management, IdP +integration, and hardened images for teams deploying MCP at scale. + +[Learn about Stacklok Enterprise →](../enterprise.mdx) + +::: + +## Troubleshooting + +
+Server fails to start + +If the server fails to start, check: + +- Is Docker, Podman, or Colima running? +- Do you have internet access to pull the container image? + +
+ +
+Client can't use the server + +If your AI client application can't use the server: + +- Make sure your client is connected to ToolHive (see Step 4) +- Restart your client to pick up the new configuration +- Verify the server is running on the **MCP Servers** page +- Disconnect and reconnect the client in ToolHive to refresh the configuration + +
+ +If you encounter any problems, please join our +[Discord community](https://discord.gg/stacklok) for help. diff --git a/versioned_docs/version-1.1/toolhive/guides-ui/registry.mdx b/versioned_docs/version-1.1/toolhive/guides-ui/registry.mdx new file mode 100644 index 00000000..f7ac4fcb --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-ui/registry.mdx @@ -0,0 +1,147 @@ +--- +title: Explore the registry +description: How to use the built-in or custom registry to find MCP servers. +--- + +import useBaseUrl from '@docusaurus/useBaseUrl'; +import ThemedImage from '@theme/ThemedImage'; + +ToolHive includes a built-in registry of MCP servers with verified +configurations that meet a +[minimum quality standard](../concepts/registry-criteria.mdx), allowing you to +discover and deploy high-quality tools effortlessly. You can browse the +registry, select servers, and run them securely through the user interface. + +You can also configure ToolHive to use a custom registry instead. This is useful +for organizations that want to maintain their own private registry of MCP +servers. + +## Find MCP servers + +The ToolHive interface includes a dedicated **Registry** section in the main +menu. This section lets you explore the various MCP servers available in either +the built-in or custom registry. + + + +The registry interface displays a list of available servers. You can browse +through the list, use search functionality, and click on any server to view +detailed information before installing it. + +## View server details + +Click on any server in the registry to view detailed information including: + +- **Server name and description**: Full description of what the server does +- **Tier badge**: Shows whether the server is **Official** or **Community** +- **Transport type**: Shows the communication method (e.g., stdio) +- **Popularity**: Star count and download statistics +- **Provenance**: Security verification status (e.g., "Provenance signed by + Sigstore") +- **Tools listed**: Names of all available tools the server provides +- **Action buttons**: **Install server** and **GitHub** (if repository + available) + +This detailed view helps you understand the server's purpose and capabilities +before adding it to your ToolHive installation. + +## Registry groups + +Registry groups allow you to organize related MCP servers and run them together +as a cohesive unit. This is particularly useful for creating team-specific +toolkits, workflow-based server collections, or environment-specific +configurations. + +The default ToolHive registry contains only individual servers. To use groups, +configure a [custom registry](../tutorials/custom-registry.mdx) that defines +them. + +### Key characteristics + +- **Optional**: Groups are entirely optional. You only need a custom registry + with groups if you want to install multiple servers together +- **Independent server definitions**: Each group contains complete server + configurations, not references to top-level servers +- **Self-contained**: Groups can define servers with the same names as + individual servers but with different configurations +- **Flexible membership**: The same server can appear in multiple groups with + different configurations + +### Browse and view groups + +When you use a custom registry that includes groups, they appear in the registry +grid alongside individual servers. Click on any group to view its name, +description, and a list of all included servers with their descriptions. From +the group details page, you can click **Install group** to begin installation. + +### Install a group + +To install a group: + +1. Click **Install group** to open the wizard +2. Configure each server following the same process as + [installing an individual server](./run-mcp-servers.mdx#configure-the-server) +3. Click **Next** after each server, or **Finish** on the last one + +The wizard guides you through each server one at a time, showing your progress +(for example, "Installing server 1 of 3"). After installation, the group appears +in the **MCP Servers** page where you can +[manage the servers together](./group-management.mdx). + +## Registry settings + +To configure your registry, ToolHive provides a dedicated **Registry** settings +section in **Settings** → **Registry** with three options: + +- **Default Registry** - Use ToolHive's built-in verified registry +- **Remote Registry (URL)** - Specify a custom URL for a remote registry hosted + on a web server +- **Local Registry (File Path)** - Specify a local file path to a JSON registry + file on your system + +When you select **Local Registry (File Path)**, an additional **Registry File +Path** field appears where you need to provide the absolute path to your local +JSON registry file (for example: `/path/myregistry/db.json`). Click **Save** to +apply your configuration. + +For detailed information on creating a custom registry, see the +[custom registry tutorial](../tutorials/custom-registry.mdx). + + + +## Next steps + +After exploring the registry and finding servers you want to use: + +- **Install servers**: Click on servers from the registry to install and + configure them for your environment +- **Customize tools**: Selectively enable or disable tools from installed + registry servers to create focused tool sets +- **Manage running servers**: Use the **MCP Servers** section to start, stop, + and monitor your installed servers +- **Create custom registries**: Set up your own private registry for + organization-specific servers + +## Related information + +- [Install ToolHive UI](./install.mdx) - Get started with ToolHive +- [Run MCP servers](./run-mcp-servers.mdx) - Install and manage servers from the + registry +- [Custom registry tutorial](../tutorials/custom-registry.mdx) - Create your own + MCP server registry +- [Registry criteria](../concepts/registry-criteria.mdx) - Quality standards for + built-in registry servers diff --git a/versioned_docs/version-1.1/toolhive/guides-ui/run-mcp-servers.mdx b/versioned_docs/version-1.1/toolhive/guides-ui/run-mcp-servers.mdx new file mode 100644 index 00000000..e2d1afe1 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-ui/run-mcp-servers.mdx @@ -0,0 +1,517 @@ +--- +title: Run MCP servers +description: How to install and run MCP servers in the ToolHive UI. +--- + +import RemoteAuthExamples from '../_partials/_remote-mcp-auth-examples.mdx'; +import Heading from '@theme/Heading'; + +ToolHive makes it easy to run and manage Model Context Protocol (MCP) servers. +This guide walks you through installing MCP servers from the registry, using +Docker images, or from source packages. + +## Install from the registry + +ToolHive includes a built-in registry of MCP servers that meet a +[minimum quality standard](../concepts/registry-criteria.mdx), allowing you to +discover and deploy high-quality tools effortlessly. Simply select one from the +list to run it securely with just a few clicks. + +### Select an MCP server + +{/* prettier-ignore */} +To install an MCP server (local Local MCP icon +or remote {/* prettier-ignore */} Remote MCP icon ) from the registry: + +1. Open the **Registry** page from the menu bar. +1. Browse or search for the MCP server you want to install. +1. Click on the desired MCP server to open its details page. Here you can review + more information about the server like its: + - Description + - Available tools + - Tier (community or official) + - Popularity (GitHub stars) + - A link to the GitHub repository for more details and usage documentation +1. Click the **Install server** button to start the installation process. + +### Configure the server + + + + Local MCP icon + {' '}Local MCP + + } + default> + +**Local MCP servers** run directly on your machine using your container runtime. + +The configuration form is pre-filled with defaults from the registry. Enter the +remaining required information and adjust any optional settings as needed: + +1. **Server name**: A unique name for the MCP server.\ + This defaults to the MCP server's name in the registry. If you want to run + multiple instances of the same MCP server, give each instance a unique name. + +1. **Group**: The group where this server will be added. [Optional]\ + Select an existing group or keep the default. Groups help you organize + servers and control client access. See + [Organize servers into groups](./group-management.mdx) for details. + +1. **Command arguments** (optional):\ + Enter any command-line arguments needed to run the MCP server. These might be + needed to pass application-specific parameters to the MCP server. Refer to + the MCP server's documentation for details. + + :::info + + We've made every effort to include sensible defaults in the registry for a + one-click experience, but some servers may require additional command-line + arguments to function correctly. + + ::: + +1. **Storage volumes** [Optional]:\ + Map files or folders from your local host to the MCP server. See + [Mount host files and folders](#volumes). [Optional] + +1. **Secrets** and **environment variables** expected by the server are + populated from the registry automatically. Required values are marked with an + asterisk (\*). + 1. **Secrets**: Enter a value to create a new secret or select an existing + secret to map to the environment variable. Secrets are stored securely and + can be used by the MCP server without exposing them in plaintext. + 2. **Environment variables**: Enter the value in the input field alongside + the variable name. These are typically used for configuration options that + do not require sensitive data. + +1. **Network isolation** [Optional]:\ + Enable network isolation to restrict the MCP server's network access. This + enhances security by limiting the server's ability to communicate with + external networks. See the [Network isolation](./network-isolation.mdx) guide + for details. + +:::note + +Refer to the MCP server's documentation for the required configuration +information, permissions, and authentication details. A link to the GitHub +repository is provided on each server's details page. + +::: + + + + Remote MCP icon + {' '}Remote MCP + + }> + +**Remote MCP servers** are hosted services that you connect to. + +The configuration form is pre-filled with defaults from the registry. Enter the +remaining required information and adjust any optional settings as needed: + +1. A unique **name** for the MCP server. [Required] +1. **Group**: The group where this server will be added. [Optional]\ + Select an existing group or keep the default. Groups help you organize + servers and control client access. See + [Organize servers into groups](./group-management.mdx) for details. +1. The **URL** of the remote MCP server. [Required] +1. The **transport protocol** that the MCP server uses. [Required]\ + ToolHive supports server-sent events (SSE) and Streamable HTTP (default) for + real-time communication. The protocol must match what the MCP server + supports. +1. **Authorization method**: Choose how ToolHive should authenticate with the + remote server.\ + The default is **Auto-Discovered**. Use this option for MCP servers that + fully implement the MCP authorization spec including dynamic client + registration (RFC7591) or for servers that do not require authentication. + ToolHive automatically: + - Discovers OAuth/OIDC endpoints + - Registers a new OAuth client + - Obtains and manages client credentials + - Handles token lifecycle automatically + + For MCP servers that require manual configuration, ToolHive supports OAuth2 + and OIDC authentication. Obtain the necessary information from the MCP + server's documentation or administrator. + + **OAuth2 authentication options:** + - **Authorize URL**: The URL where users are redirected to authenticate and + authorize access to the MCP server. [Required] + - **Token URL**: The URL where your application exchanges the authorization + code for access tokens. [Required] + - **Client ID**: Your application's identifier registered with the OAuth + provider. [Required] + - **Client secret**: The secret key that proves your application's identity. + Enter a value to create a new secret or select an existing secret from the + provider. Secrets are stored securely and can be used by the MCP server + without exposing them in plaintext. See + [Secrets management](./secrets-management.mdx) for details. [Optional] + - **Scopes**: List of permissions your application is requesting. [Optional] + - **PKCE**: Enable Proof Key for Code Exchange (RFC 7636) for enhanced + security without requiring a client secret. [Optional] + + **OIDC authentication options:** + - **Issuer URL**: The base URL of the OIDC provider. [Required] + - **Client ID**: Your application's identifier registered with the OIDC + provider. [Required] + - **Client secret**: The secret key that proves your application's identity. + Enter a value to create a new secret or select an existing secret from the + provider. Secrets are stored securely and can be used by the MCP server + without exposing them in plaintext. See + [Secrets management](./secrets-management.mdx) for details. [Optional] + - **PKCE**: Enable Proof Key for Code Exchange (RFC 7636) for enhanced + security without requiring a client secret. [Optional] + +1. The **callback port** for the authentication redirect. [Optional] + +1. **Custom headers** [Optional]:\ + Add custom HTTP headers to inject into requests to the remote MCP server. + - **Plaintext headers**: Add static key-value pairs for headers like + `X-Tenant-ID` or correlation IDs. These values are stored and transmitted + in plaintext. + - **Headers from secrets**: For sensitive data like API keys, add headers + whose values are retrieved from ToolHive's secrets manager. Enter a header + name and either select an existing secret or enter a new value to create + one. + +Click **Install server** to connect to the remote MCP server. + +
+View examples of remote MCP authentication configuration + + + +
+ +
+
+ +### Start the MCP server + +Click **Install server** to install and start the MCP server. ToolHive downloads +the Docker image, creates a container, and starts it with the specified +configuration. + +Once the server is running, you can see its status on the **MCP Servers** page. +Each server's card includes: + +- The server name +- Its status (Running or Stopped) with a toggle button to start or stop it +- A menu (︙) that includes the server's URL for AI clients that need manual + configuration, and options to: + - Open the server's GitHub repository + - View the server's logs + - Remove the server + +See [Manage MCP servers](#manage-mcp-servers) below for more details on server +states. + +## Install a custom MCP server + +You're not limited to the MCP servers in the registry. You can run remote MCP +servers by providing a URL, or your own local custom MCP servers using Docker +images or source packages. + +### Configure the server + + + + Local MCP icon + {' '}Custom local MCP + + } + default> + +On the **MCP Servers** page, click **Add an MCP server**, then choose **Add +custom local server** in the drop-down menu. Or if this is your first MCP +server, on the introductory screen. + +In the **Custom MCP server** dialog, choose [Docker image](#from-a-docker-image) +or [Package manager](#from-a-source-package). + +{/* prettier-ignore */} +From a Docker image + +Select the **Docker image** option. This allows you to run any MCP server that +is available as a Docker image in a remote registry or locally on your system. + +On the configuration form, enter: + +1. A unique **name** for the MCP server. [Required] + +1. **Group**: The group where this server will be added. [Optional]\ + Select an existing group or keep the default. Groups help you organize + servers and control client access. See + [Organize servers into groups](./group-management.mdx) for details. + +1. The **transport protocol** that the MCP server uses. [Required]\ + ToolHive supports standard input/output (`stdio`), server-sent events (SSE), + and Streamable HTTP. The protocol must match what the MCP server supports. + +1. The **target port** to expose from the container (SSE or Streamable HTTP + transports only). [Optional]\ + If the MCP server uses a specific port, enter it here. If not specified, + ToolHive will use a random port that is exposed to the container with the + `MCP_PORT` and `FASTMCP_PORT` environment variables. + +1. The Docker **image name** and tag (e.g., `my-mcp-server:latest`). [Required]\ + You can use any valid Docker image, including those hosted on Docker Hub or + other registries. + +1. **Command-line arguments** needed to run the MCP server. [Optional]\ + These are specific to the MCP server and might include transport options or + application-specific parameters. Refer to the MCP server's documentation for + details. + +1. Any **secrets** or **environment variables** required by the MCP server. + [Optional]\ + These might include API tokens, configuration options, or other sensitive + data. + - Secrets are mapped to an environment variable. Enter the variable name and + select an existing secret or enter a value to create a new one. + - For non-sensitive environment variables, enter the name and value directly. + +1. **Storage volumes** [Optional]:\ + Map files or folders from your local host to the MCP server. See + [Mount host files and folders](#volumes). [Optional] + +1. **Network isolation** [Optional]:\ + Enable network isolation to restrict the MCP server's network access. This + enhances security by limiting the server's ability to communicate with + external networks. See the [Network isolation](./network-isolation.mdx) guide + for details. + +Click **Install server** to create and start the MCP server container. + +{/* prettier-ignore */} +From a source package + +Select the **Package manager** option. This allows you to run an MCP server from +a source package. + +ToolHive downloads the MCP server's source package and builds a Docker image +on-the-fly. The following package formats are supported: + +- Node.js-based MCP servers using npm +- Python-based MCP servers available on PyPI, using the `uv` package manager +- Go-based MCP servers available on GitHub + +On the configuration form, enter: + +1. A unique **name** for the MCP server. [Required] + +1. **Group**: The group where this server will be added. [Optional]\ + Select an existing group or keep the default. Groups help you organize + servers and control client access. See + [Organize servers into groups](./group-management.mdx) for details. + +1. The **transport protocol** that the MCP server uses. [Required]\ + ToolHive supports standard input/output (`stdio`), server-sent events (SSE), + and Streamable HTTP. The protocol must match what the MCP server supports. + +1. The **target port** to expose from the container (SSE or Streamable HTTP + transports only). [Optional]\ + If the MCP server uses a specific port, enter it here. If not specified, + ToolHive will use a random port that is exposed to the container with the + `MCP_PORT` and `FASTMCP_PORT` environment variables. + +1. The package **protocol** (`npx`, `uvx`, or `go`). [Required] + +1. The **name** of the package to run. [Required] + 1. For `npx`, use the [npm](https://www.npmjs.com/) package name and version, + e.g. `my-mcp-package@latest` + 2. For `uvx`, use the [PyPI](https://pypi.org/) package name and version, + e.g. `my-mcp-package@latest` + 3. For `go`, use the GitHub repository URL with full path to the `main` + package and version, e.g. + `go://github.com/orgname/my-mcp-server/cmd/server@latest` + +1. **Command-line arguments** needed to run the MCP server. [Optional]\ + These are specific to the MCP server and might include transport options or + application-specific parameters. Refer to the MCP server's documentation for + details. + +1. Any **secrets** or **environment variables** required by the MCP server. + [Optional]\ + These might include API tokens, configuration options, or other sensitive + data. + - Secrets are mapped to an environment variable. Enter the variable name and + select an existing secret or enter a value to create a new one. + - For non-sensitive environment variables, enter the name and value directly. + +1. **Storage volumes** [Optional]:\ + Map files or folders from your local host to the MCP server. See + [Mount host files and folders](#volumes). [Optional] + +1. **Network isolation** [Optional]:\ + Enable network isolation to restrict the MCP server's network access. This + enhances security by limiting the server's ability to communicate with + external networks. See the [Network isolation](./network-isolation.mdx) guide + for details. + +Click **Install server** to create and start the MCP server container. + + + + Remote MCP icon + {' '}Custom remote MCP + + }> + +On the **MCP Servers** page, click **Add an MCP server**, then choose **Add a +remote MCP server** in the drop-down menu. + +On the configuration form, enter: + +1. A unique **name** for the MCP server. [Required] +2. **Group**: The group where this server will be added. [Optional]\ + Select an existing group or keep the default. Groups help you organize + servers and control client access. See + [Organize servers into groups](./group-management.mdx) for details. +3. The **URL** of the remote MCP server. [Required] +4. The **transport protocol** that the MCP server uses. [Required]\ + ToolHive supports server-sent events (SSE) and Streamable HTTP (default) for + real-time communication. The protocol must match what the MCP server + supports. +5. **Authorization method**: Choose how ToolHive should authenticate with the + remote server.\ + The default is **Auto-Discovered**. Use this option for MCP servers that + fully implement the MCP authorization spec including dynamic client + registration (RFC7591) or for servers that do not require authentication. + ToolHive automatically: + - Discovers OAuth/OIDC endpoints + - Registers a new OAuth client + - Obtains and manages client credentials + - Handles token lifecycle automatically + + For MCP servers that require manual configuration, ToolHive supports OAuth2 + and OIDC authentication. Obtain the necessary information from the MCP + server's documentation or administrator. + + **OAuth2 authentication options:** + - **Authorize URL**: The URL where users are redirected to authenticate and + authorize access to the MCP server. [Required] + - **Token URL**: The URL where your application exchanges the authorization + code for access tokens. [Required] + - **Client ID**: Your application's identifier registered with the OAuth + provider. [Required] + - **Client secret**: The secret key that proves your application's identity. + Enter a value to create a new secret or select an existing secret from the + provider. Secrets are stored securely and can be used by the MCP server + without exposing them in plaintext. See + [Secrets management](./secrets-management.mdx) for details. [Optional] + - **Scopes**: List of permissions your application is requesting. [Optional] + + **OIDC authentication options:** + - **Issuer URL**: The base URL of the OIDC provider. [Required] + - **Client ID**: Your application's identifier registered with the OIDC + provider. [Required] + - **Client secret**: The secret key that proves your application's identity. + Enter a value to create a new secret or select an existing secret from the + provider. Secrets are stored securely and can be used by the MCP server + without exposing them in plaintext. See + [Secrets management](./secrets-management.mdx) for details. [Optional] + - **PKCE**: Enable Proof Key for Code Exchange (RFC 7636) for enhanced + security without requiring a client secret. [Optional] + +6. The **callback port** for the authentication redirect. [Optional] + +7. **Custom headers** [Optional]:\ + Add custom HTTP headers to inject into requests to the remote MCP server. + - **Plaintext headers**: Add static key-value pairs for headers like + `X-Tenant-ID` or correlation IDs. These values are stored and transmitted + in plaintext. + - **Headers from secrets**: For sensitive data like API keys, add headers + whose values are retrieved from ToolHive's secrets manager. Enter a header + name and either select an existing secret or enter a new value to create + one. + +Click **Install server** to connect to the remote MCP server. + +
+View examples of remote MCP authentication configuration + + + +
+ +
+
+ +## Mount host files and folders \{#volumes} + +Some MCP servers need access to files on your machine. You can mount host paths +directly in the UI. + +1. In the server **Install / Configure** dialog, scroll to **Storage volumes**. +2. Use the **first row** to create your mount: + - **Host path** — choose a file or folder on your computer. + - **Container path** — where it should appear inside the server (for example, + `/data`). + - By default, mounts are in read-write mode. If you want your volume mount to + be **Read-only**, select the "Read-only access" option from the drop-down. +3. If you need additional mounts, click **Add a volume** and repeat. +4. Click **Install server** to start the server with your volume(s). + +This applies to both registry-installed servers and custom servers (Docker image +or source package). + +## Manage MCP servers + +On the **MCP Servers** page, you can manage your installed MCP servers: + +- **Start/Stop**: Use the toggle button to start or stop the MCP server. When + you stop a server, it remains in the list but is no longer running. ToolHive + removes the server from connected AI clients while stopped. + +Click the menu (︙) on the server card to access these options: + +- **Copy URL** (⧉): Copy the MCP server's URL to your clipboard. This URL is + used by AI clients to connect to the MCP server. +- **Edit configuration**: Modify the server's settings, such as command + arguments, environment variables, secrets, storage volumes, and network + isolation. The dialog is pre-filled with your existing configuration, so you + only need to change the specific settings you want to update. +- **GitHub repository**: View the MCP server's source code and documentation on + GitHub. (Only available for servers installed from the registry using the + default name.) +- **Logs**: View the server's output. For local servers, this shows the + container logs. For remote servers, it shows logs from the proxy process. +- **Remove**: Stop and remove the MCP server from ToolHive. This deletes the + container and any associated configuration, so use with caution. +- **Copy server to a group**: Duplicate the server configuration to a different + group. See [Organize servers into groups](./group-management.mdx) for details. + +When you quit the application, ToolHive prompts you to stop all running MCP +servers. The running servers are recorded and ToolHive restarts them +automatically the next time you launch the application. + +## Next steps + +- Connect ToolHive to AI clients like GitHub Copilot or Cursor using the + [client configuration guide](./client-configuration.mdx). +- Customize which tools are available from registry servers using the + [customize tools guide](./customize-tools.mdx). +- Learn more about [secrets management](./secrets-management.mdx) to securely + manage API tokens and other sensitive data. +- Test your MCP servers using the [playground](./playground.mdx) to validate + functionality and behavior with different models. diff --git a/versioned_docs/version-1.1/toolhive/guides-ui/secrets-management.mdx b/versioned_docs/version-1.1/toolhive/guides-ui/secrets-management.mdx new file mode 100644 index 00000000..99987565 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-ui/secrets-management.mdx @@ -0,0 +1,69 @@ +--- +title: Secrets management +description: How to securely manage API tokens and other sensitive data in the ToolHive UI. +--- + +Many MCP servers need secrets like API tokens, connection strings, and other +sensitive parameters. ToolHive provides built-in secrets management so you can +manage these values securely without exposing them in plaintext configuration +files. + +ToolHive encrypts secrets using a randomly generated password that is stored in +your operating system's secure keyring. + +You can add new secrets on the **Secrets** page or during MCP server +installation. + +:::note + +The ToolHive UI does not currently support the 1Password secrets provider. If +you have configured the 1Password secrets provider using the ToolHive CLI, the +UI will automatically update your configuration to use the built-in encrypted +provider instead. + +::: + +## Enter secrets during MCP installation + +When you install an MCP server from the registry, any required secrets are +listed on the configuration form with their corresponding environment variable. + +When you add a custom MCP server, add secrets by entering the environment +variable name that the MCP server expects. + +To set the secret value, you can: + +- Select an existing secret to populate its value in the configuration form. +- Enter a value in the input box. ToolHive creates a new secret with a name + matching the environment variable. + +## Manage secrets + +Your ToolHive secrets are managed on the **Secrets** page. Here you can: + +- Click the **Add secret** button to create a new secret. Enter a friendly name + for the secret and its value. +- Expand the menu (︙) next to an existing secret to: + - Update the secret value + - Delete the secret + +:::warning + +If you delete a secret that is in use by an MCP server, the server will continue +running but you will not be able to restart it if stopped. You'll need to remove +the server and reinstall it with the required secrets, or add the secret back +using the same name. + +::: + +## Next steps + +- [Restrict network access](./network-isolation.mdx) for your MCP servers to + improve security +- [Configure client access](./client-configuration.mdx) to connect your AI + clients to ToolHive + +## Related information + +- [Run MCP servers](./run-mcp-servers.mdx) +- [Client configuration](./client-configuration.mdx) diff --git a/versioned_docs/version-1.1/toolhive/guides-vmcp/audit-logging.mdx b/versioned_docs/version-1.1/toolhive/guides-vmcp/audit-logging.mdx new file mode 100644 index 00000000..3400a4c6 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-vmcp/audit-logging.mdx @@ -0,0 +1,415 @@ +--- +title: Audit logging +description: + Configure audit logging for Virtual MCP Server to meet security compliance + requirements and track MCP operations. +--- + +Virtual MCP Server (vMCP) provides comprehensive audit logging for all MCP +operations. Audit logs enable security teams to meet compliance requirements, +investigate incidents, and maintain operational visibility. + +## Overview + +The audit logging system captures structured JSON events for every MCP protocol +operation, including tool calls, resource reads, prompt requests, and composite +workflow executions. The implementation can help organizations address +audit-related compliance requirements for enterprise security. + +## Enable audit logging + +Configure audit logging in the VirtualMCPServer resource using the +`spec.config.audit` field: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp + namespace: toolhive-system +spec: + config: + groupRef: my-group + # highlight-start + audit: + enabled: true + component: vmcp-production + includeRequestData: true + includeResponseData: false + maxDataSize: 4096 + # highlight-end +``` + +### Configuration options + +| Field | Description | Default | +| --------------------- | ------------------------------------------------ | ------------- | +| `enabled` | Enable audit logging | `false` | +| `component` | Component name in audit events | `vmcp-server` | +| `eventTypes` | Specific event types to log (empty = all events) | `[]` (all) | +| `excludeEventTypes` | Event types to exclude from logging | `[]` | +| `includeRequestData` | Include request payloads in audit logs | `false` | +| `includeResponseData` | Include response payloads in audit logs | `false` | +| `maxDataSize` | Maximum payload size in bytes | `1024` | +| `logFile` | File path for audit logs (empty = stdout) | `""` (stdout) | + +## Audit event types + +vMCP logs several categories of events: + +### MCP protocol operations + +Standard MCP protocol interactions are logged with these event types: + +- `mcp_initialize`: MCP connection initialization +- `mcp_tool_call`: Tool invocation +- `mcp_tools_list`: List available tools +- `mcp_resource_read`: Read a resource +- `mcp_resources_list`: List available resources +- `mcp_prompt_get`: Get a prompt +- `mcp_prompts_list`: List available prompts +- `mcp_notification`: MCP notifications +- `mcp_completion`: Completion requests +- `mcp_roots_list_changed`: Root list changed notifications + +### Connection and transport events + +Connection establishment events: + +- `sse_connection`: SSE transport connection established (SSE connections are + logged separately due to their long-lived nature; other transports like + streamable HTTP use standard MCP protocol events) +- `mcp_ping`: Health check pings + +### Logging events + +MCP logging protocol events: + +- `mcp_logging`: Logging messages from MCP servers + +### Composite workflow operations + +Composite tools (multi-step workflows) generate additional audit events: + +- `vmcp_workflow_started`: Workflow execution begins +- `vmcp_workflow_completed`: Workflow completes successfully +- `vmcp_workflow_failed`: Workflow fails +- `vmcp_workflow_timed_out`: Workflow exceeds timeout +- `vmcp_workflow_step_started`: Individual step begins +- `vmcp_workflow_step_completed`: Individual step completes +- `vmcp_workflow_step_failed`: Individual step fails +- `vmcp_workflow_step_skipped`: Conditional step is skipped + +### Fallback event types + +Generic event types for unrecognized requests: + +- `mcp_request`: Generic MCP request when specific type cannot be determined +- `http_request`: Generic HTTP request (non-MCP) + +## Filter audit events + +By default, all event types are logged. You can filter events using `eventTypes` +(allowlist) and `excludeEventTypes` (blocklist): + +```yaml +spec: + config: + audit: + enabled: true + eventTypes: + - mcp_initialize + - mcp_tool_call + - vmcp_workflow_started + - vmcp_workflow_completed + - vmcp_workflow_failed + excludeEventTypes: + - mcp_ping +``` + +Use `eventTypes` to capture only specific operations. Use `excludeEventTypes` to +filter out high-frequency events like health checks. + +:::info + +When both fields are specified, `excludeEventTypes` takes precedence. Events +matching the exclusion list are never logged, even if they appear in +`eventTypes`. + +::: + +## Payload logging + +By default, audit logs capture metadata about operations but not the actual +request and response payloads. For forensic analysis or debugging, you can +enable payload capture: + +```yaml +spec: + config: + audit: + enabled: true + includeRequestData: true + includeResponseData: true + maxDataSize: 16384 # Truncate payloads larger than 16KB +``` + +The `maxDataSize` field controls the maximum size of captured payloads to +prevent log bloat. Payloads exceeding this limit are truncated. + +:::warning + +Request and response payloads may contain sensitive data. Review your +organization's data handling policies before enabling payload logging in +production environments. + +::: + +## Audit log format + +Each audit event is a structured JSON object with these fields: + +```json +{ + "time": "2025-02-02T15:45:30.123456789Z", + "level": "INFO+2", + "msg": "audit_event", + "audit_id": "a3f2b8d1-4c5e-6789-abcd-ef0123456789", + "type": "mcp_tool_call", + "logged_at": "2025-02-02T15:45:30.123456Z", + "outcome": "success", + "component": "vmcp-production", + "source": { + "type": "network", + "value": "10.0.1.50", + "extra": { + "user_agent": "Claude/1.0" + } + }, + "subjects": { + "user": "alice@company.com", + "user_id": "sub-alice-123", + "client_name": "Claude Desktop", + "client_version": "1.0.0" + }, + "target": { + "endpoint": "/mcp", + "method": "tools/call", + "type": "tool", + "name": "github_create_pr" + }, + "metadata": { + "extra": { + "duration_ms": 234, + "transport": "http", + "backend_name": "github" + } + }, + "data": { + "request": { + "title": "Add new feature", + "base": "main" + } + } +} +``` + +### Field descriptions + +| Field | Description | +| ----------- | --------------------------------------------------------- | +| `time` | Timestamp when the log was generated | +| `level` | Log level (INFO+2 for audit events) | +| `msg` | Always "audit_event" for audit logs | +| `audit_id` | Unique identifier for this audit event | +| `type` | Event classification (what happened) | +| `logged_at` | UTC timestamp when the event occurred | +| `outcome` | Result (success, failure, denied, error) | +| `component` | System component that generated the event | +| `source` | Request origin (IP address, user agent) | +| `subjects` | Identity information (user, client) | +| `target` | Resource or operation targeted | +| `metadata` | Additional context (duration_ms, transport, backend_name) | +| `data` | Optional request and response payloads | + +## User identity in audit logs + +The audit system extracts user identity from OIDC authentication tokens into the +subjects field (shown in the audit log format above). + +The user field is populated using this fallback order from token claims: + +1. `name` claim (full name) +2. `preferred_username` claim (username) +3. `email` claim (email address) +4. `"anonymous"` (when authentication is disabled) + +The `user_id` field contains the OIDC `sub` claim (subject identifier). + +:::note + +When using anonymous authentication (not recommended for production), audit logs +show `"user": "anonymous"` with no `user_id`. This may not meet compliance +requirements for user identification. + +::: + +## Configure output destination + +### Log to stdout (default) + +By default, audit logs are written to standard output, which integrates with +Kubernetes log collection: + +```yaml +spec: + config: + audit: + enabled: true + # logFile not specified = stdout +``` + +This approach works well with log aggregation systems like Fluentd, Fluent Bit, +or cloud provider log collectors (CloudWatch, Google Cloud Logging, Azure +Monitor). + +### Log to a file + +To persist audit logs to a file, configure a persistent volume: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp + namespace: toolhive-system +spec: + config: + audit: + enabled: true + logFile: /var/log/audit/vmcp.log + podTemplateSpec: + spec: + volumes: + - name: audit-logs + persistentVolumeClaim: + claimName: vmcp-audit-pvc + containers: + - name: vmcp + volumeMounts: + - name: audit-logs + mountPath: /var/log/audit +``` + +:::info + +File-based audit logs are written with secure permissions (`0600`) that allow +read/write access only to the file owner (the user the vMCP container runs as). + +To access these logs from persistent volumes or log collection sidecars, ensure: + +- The vMCP container user is explicitly configured via + `podTemplateSpec.spec.securityContext.runAsUser` +- Log collection sidecars run as the same user, or +- Use `podTemplateSpec.spec.securityContext.fsGroup` to enable group-based + access + +For most deployments, using the default stdout logging is simpler and integrates +better with Kubernetes log collection systems. + +::: + +## Configuration patterns + +### Security compliance + +For environments requiring comprehensive audit trails: + +```yaml +spec: + config: + audit: + enabled: true + component: vmcp-production + # No eventTypes filter = log all events (comprehensive) + excludeEventTypes: + - mcp_ping # Exclude health checks (not audit-relevant) + includeRequestData: true + includeResponseData: true + maxDataSize: 16384 + logFile: /var/log/vmcp/audit.log +``` + +### Performance-optimized + +For high-throughput environments, log only critical events: + +```yaml +spec: + config: + audit: + enabled: true + component: vmcp-high-throughput + eventTypes: + - mcp_tool_call + - vmcp_workflow_failed + includeRequestData: false + includeResponseData: false +``` + +## Query and analyze audit logs + +### Search for specific operations + +Query logs for a specific user's tool calls: + +```bash +kubectl logs -n toolhive-system -l app.kubernetes.io/instance=my-vmcp \ + | jq 'select(.type == "mcp_tool_call" and .subjects.user == "alice@company.com")' +``` + +### Analyze workflow failures + +Find all failed workflows in the last hour: + +```bash +kubectl logs -n toolhive-system -l app.kubernetes.io/instance=my-vmcp --since=1h \ + | jq 'select(.type == "vmcp_workflow_failed")' +``` + +### Track backend usage + +Count tool calls per backend MCP server: + +```bash +kubectl logs -n toolhive-system -l app.kubernetes.io/instance=my-vmcp \ + | jq -r 'select(.type == "mcp_tool_call") | .metadata.extra.backend_name' \ + | sort | uniq -c +``` + +## Integrate with log collection systems + +When audit logs are written to stdout (the default), they integrate with +standard Kubernetes log collection infrastructure. Your existing log collectors +(Fluentd, Fluent Bit, Filebeat, Splunk forwarders) can parse the JSON audit +events and route them to your observability backend. + +For detailed configuration examples and best practices for setting up log +collection with Fluentd, Filebeat, Splunk, and other systems, see the +[Kubernetes logging guide](../guides-k8s/logging.mdx#set-up-log-collection). + +## Next steps + +- [Review the vMCP configuration guide](./configuration.mdx) to revisit or + refine your VirtualMCPServer setup +- [Learn about ToolHive's observability model](../concepts/observability.mdx) + for a deeper understanding of the telemetry architecture + +## Related information + +- [Authentication](./authentication.mdx) - configure client and backend + authentication for user identity in audit logs +- [Telemetry and metrics](./telemetry-and-metrics.mdx) - monitor vMCP + performance with OpenTelemetry +- [Kubernetes logging guide](../guides-k8s/logging.mdx) - configure logging for + MCP servers in Kubernetes diff --git a/versioned_docs/version-1.1/toolhive/guides-vmcp/authentication.mdx b/versioned_docs/version-1.1/toolhive/guides-vmcp/authentication.mdx new file mode 100644 index 00000000..e116d87d --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-vmcp/authentication.mdx @@ -0,0 +1,172 @@ +--- +title: Authentication +description: Configure client and backend authentication for vMCP. +--- + +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 + +```mermaid +flowchart LR + subgraph Boundary1[" "] + direction TB + Client[MCP Client] + B1Label["**Boundary 1**
Client → vMCP"] + end + + subgraph vMCP["Virtual MCP Server (vMCP)"] + Auth[Token validation] + Backend[Backend auth] + end + + subgraph Boundary2[" "] + direction TB + B2Label["**Boundary 2**
vMCP → Backend APIs"] + GitHub[GitHub API] + Jira[Jira API] + end + + Client -->|"vMCP-scoped
token"| Auth + Auth --> Backend + Backend -->|"Backend-scoped
token"| GitHub + Backend -->|"Backend-scoped
token"| Jira +``` + +**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):** 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 vMCP. + +### Anonymous (development only) + +No authentication required: + +```yaml title="VirtualMCPServer resource" +spec: + incomingAuth: + type: anonymous +``` + +:::warning + +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 title="VirtualMCPServer resource" +spec: + incomingAuth: + type: oidc + oidcConfig: + type: inline + inline: + issuer: https://auth.example.com + 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 title="VirtualMCPServer resource" +spec: + incomingAuth: + type: oidc + oidcConfig: + type: inline + inline: + issuer: https://auth.example.com + clientId: + audience: vmcp + clientSecretRef: + name: oidc-client-secret + key: clientSecret +``` + +Create the Secret: + +```yaml +apiVersion: v1 +kind: Secret +metadata: + name: oidc-client-secret + namespace: toolhive-system +type: Opaque +stringData: + clientSecret: +``` + +### Kubernetes service account tokens + +Authenticate using Kubernetes service account tokens for in-cluster clients: + +```yaml title="VirtualMCPServer resource" +spec: + incomingAuth: + type: oidc + oidcConfig: + type: kubernetes + kubernetes: + audience: toolhive +``` + +This configuration uses the Kubernetes API server as the OIDC issuer and +validates service account tokens. The defaults work for most clusters: + +- **issuer**: `https://kubernetes.default.svc` (auto-detected) +- **audience**: `toolhive` (configurable) + +## Outgoing authentication + +Configure how vMCP authenticates to backend MCP servers. + +### Discovery mode + +When using discovery mode, vMCP checks each backend MCPServer's +`externalAuthConfigRef` to determine how to authenticate. If a backend has no +auth config, vMCP connects without authentication. + +```yaml title="VirtualMCPServer resource" +spec: + outgoingAuth: + source: discovered +``` + +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. + +## Next steps + +- [Configure tool aggregation](./tool-aggregation.mdx) to manage how tools from + multiple backends are presented to clients +- [Set up audit logging](./audit-logging.mdx) to track authentication decisions + and request activity + +## Related information + +- [Authentication framework concepts](../concepts/auth-framework.mdx) +- [Cedar policies](../concepts/cedar-policies.mdx) for detailed policy syntax +- [VirtualMCPServer configuration](./configuration.mdx) +- [Token exchange in Kubernetes](../guides-k8s/token-exchange-k8s.mdx) diff --git a/versioned_docs/version-1.1/toolhive/guides-vmcp/backend-discovery.mdx b/versioned_docs/version-1.1/toolhive/guides-vmcp/backend-discovery.mdx new file mode 100644 index 00000000..0f091f7f --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-vmcp/backend-discovery.mdx @@ -0,0 +1,740 @@ +--- +title: Backend discovery modes +description: Choose between discovered and inline backend discovery for Virtual MCP Server. +--- + +Virtual MCP Server (vMCP) supports two backend discovery modes, allowing you to +optimize for either operational convenience (discovered mode with declarative +backend management) or security (inline mode with minimal permissions). + +## Overview + +The deployment mode is configured via the `spec.outgoingAuth.source` field in +the VirtualMCPServer resource. + +| Mode | Backend Discovery | RBAC Requirements | K8s API Access | +| ------------ | ------------------------- | -------------------------------------- | ------------------ | +| `discovered` | Runtime from K8s API | Namespace-scoped read + status updates | Yes | +| `inline` | Inline from configuration | Minimal (status updates only) | No (except status) | + +:::note[Design rationale] + +Backend discovery is configured via `outgoingAuth.source` because the +authentication strategy and backend source are tightly coupled: + +- **Discovered backends** (`source: discovered`) are found at runtime by + querying the Kubernetes API and can reference MCPExternalAuthConfig resources + for their authentication +- **Inline backends** (`source: inline`) are defined in the configuration and + require their authentication to be explicitly configured in + `outgoingAuth.backends` + +This coupling ensures authentication configuration matches the backend discovery +method, preventing misconfigurations. + +::: + +### When to use discovered mode + +Choose discovered mode when: + +- Backends change frequently and you want declarative management via Kubernetes + resources +- Centralized authentication via MCPExternalAuthConfig is preferred +- Namespace-scoped read permissions are acceptable + +### When to use inline mode + +Choose inline mode when: + +- Security or compliance requires minimal permissions and attack surface +- Backend configuration is stable and changes infrequently +- Explicit control over all backend details is required (zero-trust, air-gapped + environments) + +### Trade-offs comparison + +| Consideration | Discovered Mode | Inline Mode | +| ------------------------------ | ------------------------------------------------ | ----------------------------------------- | +| Backend management | Declarative (K8s resources) | Explicit (in configuration) | +| Configuration changes | Add/remove resources without vMCP config changes | Update vMCP config and restart | +| RBAC permissions | Namespace read access | Minimal (status updates only) | +| Attack surface | Larger (K8s API access) | Smaller (no backend discovery API access) | +| Auth management | Centralized (MCPExternalAuthConfig) | Duplicated in YAML | +| Individual backend pod updates | Supported without vMCP changes | Requires vMCP awareness | + +## Discovered mode + +Discovered mode queries the Kubernetes API at runtime to find backend MCP +servers. This is the default mode. + +### Discovered mode configuration + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp + namespace: toolhive-system +spec: + config: + groupRef: my-group + incomingAuth: + type: anonymous + outgoingAuth: + source: discovered # Discover backends at runtime (default) +``` + +### How it works + +When vMCP starts in discovered mode: + +1. **Group verification**: Verifies the referenced MCPGroup exists +2. **Workload discovery**: Queries all MCPServer and MCPRemoteProxy resources in + the group +3. **Backend conversion**: For each workload: + - Extracts service URL and transport type + - Resolves authentication from `externalAuthConfigRef` if configured + - Adds metadata labels +4. **Capability querying**: Calls each backend's `initialize` method to discover + available tools, resources, and prompts +5. **Status updates**: Reports backend health in the VirtualMCPServer status + +### Discovered mode RBAC + +The operator automatically creates the required RBAC resources (ServiceAccount, +Role, and RoleBinding) when you create a VirtualMCPServer resource. + +For reference, the vMCP service account needs read access to: + +- `configmaps`, `secrets`: Read OIDC configs and auth secrets +- `mcpgroups`: Verify group exists and list members +- `mcpservers`, `mcpremoteproxies`: Discover backend workloads +- `mcpexternalauthconfigs`: Resolve authentication configurations +- `mcptoolconfigs`: Resolve tool filtering and renaming +- `virtualmcpservers/status`: Update status with discovered backends + +### Runtime updates + +When backend resources are added, modified, or removed in the group: + +1. The change does NOT automatically trigger vMCP to rediscover backends +2. vMCP continues using the backend list from startup +3. To pick up changes, restart the vMCP pod: + +```bash +kubectl rollout restart deployment vmcp-my-vmcp -n toolhive-system +``` + +## Inline mode + +Inline mode uses pre-configured backends defined in the VirtualMCPServer +resource. This eliminates the need for Kubernetes API access (except status +updates), reducing the attack surface. + +### Inline mode configuration + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp + namespace: toolhive-system +spec: + config: + groupRef: my-group + # highlight-start + backends: + - name: github-mcp + url: http://github-mcp.toolhive-system.svc.cluster.local:8080 + transport: sse + - name: fetch-mcp + url: http://fetch-mcp.toolhive-system.svc.cluster.local:8080 + transport: streamable-http + # highlight-end + incomingAuth: + type: anonymous + outgoingAuth: + # highlight-start + source: inline # Use inline backend configuration + # highlight-end + backends: + github-mcp: + type: external_auth_config_ref + externalAuthConfigRef: + name: github-token-config +``` + +:::note + +The `groupRef` field is required in both discovered and inline modes. In inline +mode, the group reference is used for organizational purposes and status +reporting, even though backends are defined inline rather than discovered from +the group. + +::: + +:::info + +Backend authentication in `outgoingAuth.backends` uses references to +[MCPExternalAuthConfig](../reference/crd-spec.md#apiv1alpha1mcpexternalauthconfig) +resources, not inline configuration. Create the MCPExternalAuthConfig resource +first, then reference it by name. See the +[Authentication guide](./authentication.mdx) for complete examples. + +::: + +### Backend configuration + +Each backend in `spec.config.backends` requires: + +| Field | Description | Required | +| ----------- | -------------------------------------------------------- | -------- | +| `name` | Backend identifier (must match auth config keys) | Yes | +| `url` | Backend MCP server URL (must be `http://` or `https://`) | Yes | +| `transport` | MCP transport protocol (`sse` or `streamable-http`) | Yes | +| `metadata` | Custom labels for the backend | No | + +### Inline mode RBAC + +Inline mode requires minimal permissions: + +- `virtualmcpservers/status`: Update status (only permission needed) + +The operator still creates RBAC resources for status updates, but the vMCP pod +does not query the Kubernetes API for backend discovery. + +## Verify backend status + +### Check VirtualMCPServer status + +View discovered backends and their health: + +```bash +kubectl get virtualmcpserver my-vmcp -n toolhive-system -o yaml +``` + +The status includes: + +```yaml +status: + phase: Ready # Pending|Ready|Degraded|Failed + backendCount: 2 + discoveredBackends: + - name: github-mcp + url: http://github-mcp.toolhive-system.svc.cluster.local:8080 + status: ready + authConfigRef: github-token-config + authType: token_exchange + lastHealthCheck: '2025-02-02T15:30:00Z' + message: Healthy + circuitBreakerState: closed + circuitLastChanged: '2025-02-02T10:00:00Z' + consecutiveFailures: 0 + - name: fetch-mcp + url: http://fetch-mcp.toolhive-system.svc.cluster.local:8080 + status: ready + authType: unauthenticated + lastHealthCheck: '2025-02-02T15:30:00Z' + message: Healthy + consecutiveFailures: 0 +``` + +### Query the status endpoint + +vMCP exposes an unauthenticated `/status` HTTP endpoint for operational +monitoring: + +```bash +kubectl port-forward -n toolhive-system svc/vmcp-my-vmcp 4483:4483 +curl http://localhost:4483/status +``` + +Response format: + +```json +{ + "backends": [ + { + "name": "github-mcp", + "health": "healthy", + "transport": "sse", + "auth_type": "token_exchange" + }, + { + "name": "fetch-mcp", + "health": "healthy", + "transport": "streamable-http", + "auth_type": "unauthenticated" + } + ], + "healthy": true, + "version": "v1.2.3", + "group_ref": "my-group" +} +``` + +**Health states:** + +| State | Description | CRD Status | +| ----------------- | ------------------------------------------------------------------------------------------------------------ | ------------- | +| `healthy` | Backend responds to health checks successfully and quickly (under 5s) | `ready` | +| `degraded` | Health checks succeed but response times exceed 5 seconds (slow), or backend recently recovered from failure | `degraded` | +| `unhealthy` | Backend not responding or health checks timing out (timeout controlled by `healthCheckTimeout`, default 10s) | `unavailable` | +| `unauthenticated` | Authentication to backend failed (internal tracking only) | `unavailable` | +| `unknown` | Health check not yet performed (initial state) | `unknown` | + +:::note[Status terminology] + +The `/status` HTTP endpoint uses internal health values (`healthy`, `degraded`, +`unhealthy`, `unauthenticated`, `unknown`) for debugging. + +The VirtualMCPServer CRD uses user-facing status values (`ready`, `degraded`, +`unavailable`, `unknown`) as shown in the "CRD Status" column above. + +Note: `unauthenticated` is tracked separately for diagnostics but represents an +authentication failure reason, not a distinct health state—it maps to +`unavailable` in the CRD. + +::: + +:::info[Unauthenticated access] + +The `/status` endpoint is unauthenticated for operator consumption. It exposes +operational metadata but does not include secrets, tokens, internal URLs, or +request data. + +::: + +:::tip + +To configure health check intervals, timeouts, thresholds, and circuit breaker +settings, see the +[Operational configuration](./configuration.mdx#operational-configuration) +section. + +::: + +## Switch deployment modes + +Switching between modes requires updating the VirtualMCPServer resource and +restarting the vMCP pod. + +### From discovered to inline + +1. List current backends to capture their configuration: + + ```bash + kubectl get virtualmcpserver my-vmcp -n toolhive-system \ + -o json | jq '.status.discoveredBackends' + ``` + +2. Update the VirtualMCPServer to inline mode: + + ```yaml + spec: + config: + groupRef: my-group + backends: + - name: github-mcp + url: http://github-mcp.toolhive-system.svc.cluster.local:8080 + transport: sse + # Add all backends from status.discoveredBackends + outgoingAuth: + source: inline + ``` + +3. The operator automatically restarts the vMCP pod with the new configuration + +4. Optionally reduce RBAC permissions by removing read access to MCPServer and + MCPRemoteProxy resources (keep status update permissions) + +### From inline to discovered + +1. Ensure backend MCPServer and MCPRemoteProxy resources exist in the group + +2. Update the VirtualMCPServer to discovered mode: + + ```yaml + spec: + config: + groupRef: my-group + # Remove backends array + outgoingAuth: + source: discovered + ``` + +3. Verify RBAC permissions are configured (operator creates them automatically) + +4. The operator automatically restarts the vMCP pod with the new configuration + +5. Check status to verify backends were discovered: + + ```bash + kubectl get virtualmcpserver my-vmcp -n toolhive-system \ + -o json | jq '.status.discoveredBackends' + ``` + +## Complete example + +Here's a complete example showing all required resources for discovered mode +with authentication: + +```yaml +--- +# 1. Create the MCPGroup +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPGroup +metadata: + name: engineering-tools + namespace: toolhive-system +spec: + description: Engineering team MCP servers + +--- +# 2. Create authentication config for GitHub backend +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPExternalAuthConfig +metadata: + name: github-token-config + namespace: toolhive-system +spec: + type: tokenExchange + tokenExchange: + tokenUrl: https://oauth.example.com/token + clientId: github-mcp-client + clientSecretRef: + name: github-oauth-secret + key: client-secret + audience: github-api + +--- +# 3. Create backend MCPServer +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: github-mcp + namespace: toolhive-system +spec: + groupRef: engineering-tools + image: ghcr.io/example/github-mcp-server:v1.2.3 + transport: sse + externalAuthConfigRef: + name: github-token-config + +--- +# 4. Create VirtualMCPServer (discovered mode) +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: engineering-vmcp + namespace: toolhive-system +spec: + config: + groupRef: engineering-tools + incomingAuth: + type: oidc + oidc: + issuer: https://auth.company.com + audience: engineering-vmcp + outgoingAuth: + source: discovered # Discovers github-mcp and its auth config +``` + +Apply all resources: + +```bash +kubectl apply -f vmcp-complete-example.yaml +``` + +Verify backends were discovered: + +```bash +kubectl get virtualmcpserver engineering-vmcp -n toolhive-system \ + -o json | jq '.status.discoveredBackends' +``` + +## Troubleshooting + +
+Backends not appearing in status + +**Symptoms:** + +- `status.discoveredBackends` is empty or missing backends +- `status.backendCount` is 0 or lower than expected + +**Possible causes and solutions:** + +1. **MCPGroup not in Ready state** + + ```bash + kubectl get mcpgroup my-group -n toolhive-system + ``` + + Wait for the group to reach Ready state before starting vMCP. + +2. **Backend resources not referencing the correct group** + + ```bash + kubectl get mcpserver,mcpremoteproxy -n toolhive-system \ + -o custom-columns=NAME:.metadata.name,GROUP:.spec.groupRef + ``` + + Ensure all backends have `spec.groupRef` matching the VirtualMCPServer's + `spec.config.groupRef`. + +3. **vMCP pod not restarted after backend changes** + + Backend changes require a pod restart to be discovered: + + ```bash + kubectl rollout restart deployment vmcp-my-vmcp -n toolhive-system + ``` + +4. **RBAC permissions missing (discovered mode)** + + Check the vMCP service account has required permissions: + + ```bash + kubectl get role -n toolhive-system | grep vmcp + kubectl describe role vmcp-my-vmcp -n toolhive-system + ``` + + The operator should create these automatically. If missing, delete and + recreate the VirtualMCPServer resource. + +
+ +
+Backends showing as unavailable + +**Symptoms:** + +- `status.discoveredBackends[].status` is `unavailable` or `unknown` +- `/status` endpoint shows `health: unhealthy` + +**Possible causes and solutions:** + +1. **Backend pod not running** + + ```bash + kubectl get pods -n toolhive-system -l app.kubernetes.io/name=my-backend + ``` + + Check backend pod logs for errors: + + ```bash + kubectl logs -n toolhive-system deployment/my-backend + ``` + +2. **Backend service not accessible** + + Test connectivity from vMCP pod: + + ```bash + kubectl exec -n toolhive-system deployment/vmcp-my-vmcp -- \ + wget -O- http://my-backend:8080/health + ``` + +3. **Authentication failing** + + Check vMCP logs for auth errors: + + ```bash + kubectl logs -n toolhive-system deployment/vmcp-my-vmcp | grep ERROR + ``` + + Common auth issues: + - Invalid OIDC configuration in MCPExternalAuthConfig + - Expired or invalid client secrets + - Token exchange endpoint unreachable + +4. **Backend returning errors on initialize** + + The backend may be misconfigured or failing to start properly. Check backend + logs and ensure it responds correctly to MCP `initialize` requests. + +
+ +
+RBAC permission errors + +**Symptoms:** + +- vMCP logs show `forbidden` or `unauthorized` errors +- Backends not being discovered in discovered mode + +**Error examples:** + +```text +Failed to list MCPServers: mcpservers.toolhive.stacklok.dev is forbidden: +User "system:serviceaccount:toolhive-system:vmcp-my-vmcp" cannot list +resource "mcpservers" +``` + +**Solutions:** + +1. **Verify service account and role binding exist** + + ```bash + kubectl get serviceaccount vmcp-my-vmcp -n toolhive-system + kubectl get role vmcp-my-vmcp -n toolhive-system + kubectl get rolebinding vmcp-my-vmcp -n toolhive-system + ``` + +2. **Check role permissions** + + ```bash + kubectl describe role vmcp-my-vmcp -n toolhive-system + ``` + + Required permissions for discovered mode: + - `configmaps`, `secrets`: get, list, watch + - `mcpgroups`, `mcpservers`, `mcpremoteproxies`: get, list, watch + - `mcpexternalauthconfigs`, `mcptoolconfigs`: get, list, watch + - `virtualmcpservers/status`: update, patch + +3. **Recreate RBAC resources** + + If RBAC resources are missing or incorrect, delete and recreate the + VirtualMCPServer: + + ```bash + kubectl delete virtualmcpserver my-vmcp -n toolhive-system + kubectl apply -f my-vmcp.yaml + ``` + + The operator will recreate all RBAC resources automatically. + +
+ +
+Mode switching issues + +**Symptoms:** + +- vMCP pod fails to start after switching modes +- Configuration validation errors + +**Switching from discovered to inline:** + +Ensure you define `spec.config.backends[]` before changing `source` to `inline`: + +```yaml +spec: + config: + backends: [] # ❌ Empty array will fail validation +``` + +**Switching from inline to discovered:** + +Remove the `spec.config.backends[]` array when switching to discovered mode: + +```yaml +spec: + config: + backends: [...] # ❌ Should be removed in discovered mode +``` + +
+ +
+Health check failures + +**Symptoms:** + +- `/status` endpoint shows backends as `degraded` or `unhealthy` +- Intermittent backend availability + +**Possible causes:** + +1. **Backend service overloaded or slow** + + Health checks timeout after 10 seconds (default `healthCheckTimeout`). If + backends are slow to respond, they'll be marked unhealthy even if functional. + +2. **Network issues between vMCP and backends** + + Check network policies and service mesh configuration that might block or + slow connections. + +3. **Backend requires authentication for initialize** + + Ensure `externalAuthConfigRef` is properly configured if the backend requires + authentication. + +
+ +
+Configuration validation errors + +**Missing groupRef:** + +```text +Error: spec.config.groupRef is required +``` + +**Fix:** Add `spec.config.groupRef` referencing an existing MCPGroup. + +**Invalid backend URL in inline mode:** + +```text +Error: spec.config.backends[0].url must start with http:// or https:// +``` + +**Fix:** Ensure backend URLs use proper scheme: + +```yaml +backends: + - name: my-backend + url: http://my-backend.default.svc.cluster.local:8080 # Valid + # url: my-backend:8080 # Invalid +``` + +**Missing backends array in inline mode:** + +```text +Error: spec.config.backends is required when outgoingAuth.source is "inline" +``` + +**Fix:** Define at least one backend in `spec.config.backends` when using inline +mode. + +**Invalid transport protocol:** + +```text +Error: spec.config.backends[0].transport must be "sse" or "streamable-http" +``` + +**Fix:** Use only supported transport protocols: + +```yaml +backends: + - name: my-backend + transport: sse # Valid + # transport: stdio # Invalid for inline backends +``` + +**Referenced MCPExternalAuthConfig not found:** + +```text +Error: MCPExternalAuthConfig "github-token-config" not found in namespace "toolhive-system" +``` + +**Fix:** Create the MCPExternalAuthConfig resource before referencing it, or +remove the auth reference. + +
+ +## Next steps + +- [Set up authentication](./authentication.mdx) for client-to-vMCP and + vMCP-to-backend security +- [Configure tool aggregation](./tool-aggregation.mdx) to manage how tools from + multiple backends are presented to clients + +## Related information + +- [Configure vMCP servers](./configuration.mdx) +- [VirtualMCPServer CRD specification](../reference/crd-spec.md#apiv1alpha1virtualmcpserver) diff --git a/versioned_docs/version-1.1/toolhive/guides-vmcp/composite-tools.mdx b/versioned_docs/version-1.1/toolhive/guides-vmcp/composite-tools.mdx new file mode 100644 index 00000000..f4b10c2c --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-vmcp/composite-tools.mdx @@ -0,0 +1,642 @@ +--- +title: Composite tools and workflows +description: Create multi-step workflows that span multiple backend MCP servers. +--- + +Composite tools let you define multi-step workflows that execute across multiple +backend MCP servers with parallel execution, conditional logic, approval gates, +and error handling. + +## Overview + +A composite tool combines multiple backend tool calls into a single workflow. +When a client calls a composite tool, vMCP orchestrates the execution across +backend MCP servers, handling dependencies and collecting results. + +## Key capabilities + +- **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 +- **Timeouts**: Workflow and per-step timeout configuration + +:::info + +Elicitation (user prompts during workflow execution) is defined in the CRD but +has not been extensively tested. Test thoroughly in non-production environments +first. + +::: + +## Configuration location + +Composite tools are defined in the VirtualMCPServer resource under +`spec.config.compositeTools`: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp +spec: + incomingAuth: + type: anonymous + config: + groupRef: my-tools + # ... other configuration ... + compositeTools: + - name: my_workflow + description: A multi-step workflow + parameters: + # Input parameters (JSON Schema) + steps: + # Workflow steps +``` + +For complex, reusable workflows, you can also reference external +`VirtualMCPCompositeToolDefinition` resources using +`spec.config.compositeToolRefs`. + +## Simple example + +Here's a composite tool that searches arXiv for papers on a topic and reads the +top result. This example assumes you have an MCPServer resource named `arxiv` in +a group that your vMCP server references, and that you're using the default +[conflict resolution strategy](./tool-aggregation.mdx#conflict-resolution-strategies) +and prefix format (`_`): + +```yaml title="VirtualMCPServer resource" +spec: + config: + compositeTools: + - name: research_topic + description: Search arXiv for papers and read the top result + parameters: + type: object + properties: + query: + type: string + description: Research topic to search for + required: + - query + steps: + # Step 1: Search arXiv for papers matching the query + - id: search + tool: arxiv_search_papers + arguments: + query: '{{.params.query}}' + max_results: 1 + # Step 2: Download the paper (required before reading) + # Note: fromJson is needed when the MCP server returns JSON as text + # rather than structured content. This is common for servers that + # don't fully support MCP's structuredContent field. + - id: download + tool: arxiv_download_paper + arguments: + paper_id: '{{(index (fromJson .steps.search.output.text).papers 0).id}}' + dependsOn: [search] + # Step 3: Read the downloaded paper content + - id: read + tool: arxiv_read_paper + arguments: + paper_id: '{{(index (fromJson .steps.search.output.text).papers 0).id}}' + dependsOn: [download] +``` + +**What's happening:** + +1. **Parameters**: Define the workflow inputs (`query` for the research topic) +2. **Step 1 (search)**: Calls `arxiv_search_papers` with the query from + parameters using template syntax `{{.params.query}}` +3. **Step 2 (download)**: Waits for search (`dependsOn: [search]`), then + downloads the paper. The `fromJson` function parses the JSON text returned by + the server, and `index` accesses the first paper's ID. +4. **Step 3 (read)**: Waits for download, then reads the paper content. + +When a client calls this composite tool, vMCP executes all three steps in +sequence and returns the paper content. + +## Structured content vs JSON text + +MCP servers can return data in two ways: + +- **Structured content**: Data is in `structuredContent` and can be accessed + directly: `{{.steps.stepid.output.field}}` +- **JSON text**: Data is returned as a JSON string in the `text` field and + requires parsing: `{{(fromJson .steps.stepid.output.text).field}}` + +The arxiv-mcp-server in this example uses JSON text, so we use `fromJson`. Check +your backend's response format to determine which approach to use. + +## Use cases + +### Incident investigation + +Gather data from multiple monitoring systems in parallel: + +```yaml title="VirtualMCPServer resource" +spec: + config: + 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 title="VirtualMCPServer resource" +spec: + config: + 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 backend MCP servers: + +```yaml title="VirtualMCPServer resource" +spec: + config: + compositeTools: + - name: security_scan_report + description: Run security scans and create consolidated report + parameters: + type: object + properties: + package_name: + type: string + ecosystem: + type: string + repo: + type: string + required: + - package_name + - ecosystem + - repo + steps: + - id: vulnerability_scan + tool: osv_query_vulnerability + arguments: + package_name: '{{.params.package_name}}' + ecosystem: '{{.params.ecosystem}}' + - 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: 'Vulnerability scan completed for {{.params.package_name}}' + dependsOn: [vulnerability_scan, secret_scan] + onError: + action: continue +``` + +## Workflow definition + +### Parameters + +Define input parameters using JSON Schema format: + +```yaml title="VirtualMCPServer resource" +spec: + config: + 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 title="VirtualMCPServer resource" +spec: + config: + 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 +``` + +The `tool` field specifies which MCP server tool to call. This depends on your +[conflict resolution strategy](./tool-aggregation.mdx#conflict-resolution-strategies) +and prefix format. For example, if you have a tool named `search` in an MCP +server named `arxiv`, and you're using the default prefix format, you would +reference it as `arxiv_search`. + +:::tip + +When using the `condition` field, downstream steps that reference the +conditional step's output may require +[default step outputs](#default-step-outputs) to handle cases where the +condition evaluates to false. + +::: + +### Elicitation (user prompts) + +Request input from users during workflow execution: + +```yaml title="VirtualMCPServer resource" +spec: + config: + compositeTools: + - name: + steps: + - id: approval + type: elicitation + message: 'Proceed with deployment?' + schema: + type: object + properties: + confirm: { type: boolean } + timeout: '5m' +``` + +### Error handling + +Configure behavior when steps fail: + +| Action | Description | +| ---------- | ------------------------------- | +| `abort` | Stop workflow immediately | +| `continue` | Log error, proceed to next step | +| `retry` | Retry with exponential backoff | + +```yaml title="VirtualMCPServer resource" +spec: + config: + compositeTools: + - name: + steps: + - id: + # ... other step config (tool, arguments, etc.) + onError: + action: retry + retryCount: 3 +``` + +:::tip + +When using `onError.action: continue`, downstream steps that reference this +step's output may require [default step outputs](#default-step-outputs) to +handle cases where the step fails. + +::: + +### Default step outputs + +When steps can be skipped (due to `condition` being false or +`onError.action: continue`), downstream steps that reference their outputs need +fallback values. Use `defaultResults` to provide these values. + +#### When defaultResults are required + +You must provide `defaultResults` when **both** of these conditions are true: + +1. A step can be skipped (has a `condition` field or `onError.action: continue`) +2. A downstream step references the skipped step's output in its arguments + +#### Configuration + +Define default values that match the expected output structure: + +```yaml title="VirtualMCPServer resource" +spec: + config: + compositeTools: + - name: optional_security_check + description: Run security scan with optional vulnerability check + parameters: + type: object + properties: + package_name: + type: string + ecosystem: + type: string + run_vuln_scan: + type: boolean + default: false + required: + - package_name + - ecosystem + steps: + # Step 1: Optional vulnerability scan + - id: vuln_scan + tool: osv_query_vulnerability + arguments: + package_name: '{{.params.package_name}}' + ecosystem: '{{.params.ecosystem}}' + condition: '{{.params.run_vuln_scan}}' + # highlight-start + defaultResults: + vulns: [] + # highlight-end + # Step 2: Create report using scan results + - id: create_report + tool: docs_create_document + arguments: + title: 'Security Report' + # This references vuln_scan output, so defaultResults are needed + body: 'Found {{len .steps.vuln_scan.output.vulns}} vulnerabilities' + dependsOn: [vuln_scan] +``` + +#### Continue on error example + +When using `onError.action: continue`, provide defaults for potential failures: + +```yaml title="VirtualMCPServer resource" +spec: + config: + compositeTools: + - name: multi_source_data + description: Gather data from multiple sources, continue on failures + steps: + # Step 1: Fetch from primary source (may fail) + - id: fetch_primary + tool: api_get_data + arguments: + source: 'primary' + onError: + action: continue + # highlight-start + defaultResults: + status: 'unavailable' + data: null + # highlight-end + # Step 2: Aggregate results + - id: aggregate + tool: processing_combine_data + arguments: + # Uses fetch_primary output even if it failed + primary: '{{.steps.fetch_primary.output.data}}' + dependsOn: [fetch_primary] +``` + +#### Validation + +vMCP validates `defaultResults` at configuration time: + +- **Missing defaults**: If a step can be skipped and downstream steps reference + its output, but `defaultResults` is not provided, vMCP returns a validation + error +- **Structure**: The `defaultResults` value can be any valid JSON type (object, + array, string, number, boolean, null) +- **No type checking**: vMCP does not verify that `defaultResults` match the + actual output structure—you must ensure they match the format your downstream + steps expect + +#### Example validation error + +```yaml +# This will fail validation +steps: + - id: conditional_step + tool: backend_fetch + condition: '{{.params.enabled}}' + # Missing defaultResults! + - id: use_result + tool: backend_process + arguments: + # References conditional_step output + data: '{{.steps.conditional_step.output.value}}' + dependsOn: [conditional_step] +``` + +**Error message:** + +```text +step 'conditional_step' can be skipped but is referenced by downstream steps +without defaultResults defined +``` + +## Template syntax + +Access workflow context in arguments: + +| Template | Description | +| --------------------------- | ------------------------------------------ | +| `{{.params.name}}` | Input parameter | +| `{{.steps.id.output}}` | Step output (map) | +| `{{.steps.id.output.text}}` | Text content from step output | +| `{{.steps.id.content}}` | Elicitation response content | +| `{{.steps.id.action}}` | Elicitation action (accept/decline/cancel) | + +### Template functions + +The following functions are available for use in templates: + +| Function | Description | Example | +| ---------- | -------------------------------- | -------------------------------------------- | +| `fromJson` | Parse a JSON string into a value | `{{(fromJson .steps.s1.output.text).field}}` | +| `json` | Encode a value as a JSON string | `{{json .steps.s1.output}}` | +| `quote` | Quote a string value | `{{quote .params.name}}` | +| `index` | Access array elements by index | `{{index .steps.s1.output.items 0}}` | + +All +[Go template built-in functions](https://pkg.go.dev/text/template#hdr-Functions) +are also supported (e.g., `len`, `eq`, `and`, `or`, `printf`). + +### Accessing step outputs + +When an MCP server returns structured content, you can access output fields +directly: + +```yaml +# Direct access when server supports structuredContent +result: '{{.steps.fetch.output.data}}' +items: '{{index .steps.search.output.results 0}}' +``` + +This is the simplest approach and works when the backend MCP server populates +the `structuredContent` field in its response. + +### Working with JSON text responses + +Some MCP servers return structured data as JSON text rather than using MCP's +`structuredContent` field. When this happens, use `fromJson` to parse it: + +```yaml +# Parse JSON text and access a nested field +paper_id: '{{(index (fromJson .steps.search.output.text).papers 0).id}}' +``` + +This pattern: + +1. Gets the text output: `.steps.search.output.text` +2. Parses it as JSON: `fromJson ...` +3. Accesses the `papers` array and gets the first element: `index ... 0` +4. Gets the `id` field: `.id` + +**How to tell which approach to use:** Call the backend tool directly and +inspect the response. If `structuredContent` contains your data fields, use +direct access. If `structuredContent` only has a `text` field containing JSON, +use `fromJson`. + +## Complete example + +A VirtualMCPServer with an inline composite tool using the +[arxiv-mcp-server](https://github.com/blazickjp/arxiv-mcp-server): + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: research-vmcp + namespace: toolhive-system +spec: + incomingAuth: + type: anonymous + config: + groupRef: research-tools + aggregation: + conflictResolution: prefix + conflictResolutionConfig: + prefixFormat: '{workload}_' + compositeTools: + - name: research_topic + description: Search arXiv for papers and read the top result + parameters: + type: object + properties: + query: + type: string + description: Research topic to search for + required: + - query + steps: + - id: search + tool: arxiv_search_papers + arguments: + query: '{{.params.query}}' + max_results: 1 + - id: download + tool: arxiv_download_paper + arguments: + paper_id: '{{(index (fromJson .steps.search.output.text).papers 0).id}}' + dependsOn: [search] + - id: read + tool: arxiv_read_paper + arguments: + paper_id: '{{(index (fromJson .steps.search.output.text).papers 0).id}}' + dependsOn: [download] + timeout: '5m' +``` + +> Note: The example above assumes you have: +> +> - An `MCPGroup` named `research-tools`. +> - An `arxiv-mcp-server` deployed as an `MCPServer` or `MCPRemoteProxy` +> resource that references the `research-tools` group. +> +> For a complete example of configuring MCP groups and backend servers, see the +> quickstart and tool aggregation guides. For complex, reusable workflows, +> create `VirtualMCPCompositeToolDefinition` resources and reference them with +> `spec.config.compositeToolRefs`: + +```yaml title="VirtualMCPServer resource" +spec: + config: + groupRef: my-tools + compositeToolRefs: + - name: my-reusable-workflow + - name: another-workflow +``` + +## Next steps + +- [Configure failure handling](./failure-handling.mdx) for circuit breakers and + partial failure modes in multi-backend setups + +## Related information + +- [Configure vMCP servers](./configuration.mdx) diff --git a/versioned_docs/version-1.1/toolhive/guides-vmcp/configuration.mdx b/versioned_docs/version-1.1/toolhive/guides-vmcp/configuration.mdx new file mode 100644 index 00000000..c9ecfacb --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-vmcp/configuration.mdx @@ -0,0 +1,301 @@ +--- +title: Configure vMCP servers +description: How to configure a Virtual MCP Server for common scenarios. +--- + +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.md#apiv1alpha1virtualmcpserver). + +## Create an MCPGroup + +Before creating a VirtualMCPServer, you need an +[MCPGroup](../reference/crd-spec.md#apiv1alpha1mcpgroup) to organize the backend +MCP servers. An MCPGroup is a logical container that groups related MCPServer +and MCPRemoteProxy 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 stafrt. Backend resources +reference this group using the `groupRef` field in their spec. + +## Add backends to a group + +vMCP supports two types of backends that can be added to an MCPGroup: + +### MCPServer (local containers) + +MCPServer resources run container-based MCP servers in your cluster: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: fetch + namespace: toolhive-system +spec: + groupRef: my-group # Reference to the MCPGroup + image: ghcr.io/stackloklabs/gofetch/server + transport: streamable-http +``` + +### MCPRemoteProxy (remote servers) + +MCPRemoteProxy resources proxy external remote MCP servers. They can be added to +an MCPGroup for discovery by vMCP: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRemoteProxy +metadata: + name: context7-proxy + namespace: toolhive-system +spec: + groupRef: my-group # Reference to the MCPGroup + remoteURL: https://mcp.context7.com/mcp + transport: streamable-http + proxyPort: 8080 + + # Validate incoming requests + oidcConfig: + type: inline + inline: + issuer: https://auth.company.com + audience: context7-proxy +``` + +:::caution[Current limitation] + +vMCP can discover MCPRemoteProxy backends in a group, but authentication between +vMCP and MCPRemoteProxy is not yet fully implemented. This limitation will be +addressed in a future release. See +[Proxy remote MCP servers](../guides-k8s/remote-mcp-proxy.mdx#use-with-virtual-mcp-server) +for details. + +::: + +For complete MCPRemoteProxy configuration options, see +[Proxy remote MCP servers](../guides-k8s/remote-mcp-proxy.mdx). + +## Create a VirtualMCPServer + +At minimum, a VirtualMCPServer requires a reference to an MCPGroup (via +`config.groupRef`) and an authentication type: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp + namespace: toolhive-system +spec: + config: + groupRef: my-group + incomingAuth: + type: anonymous # Disables authentication; do not use in production +``` + +The MCPGroup must exist in the same namespace and be in a Ready state before the +VirtualMCPServer can start. By default, vMCP automatically discovers and +aggregates all MCPServer and MCPRemoteProxy resources in the referenced group. +You can also define backends explicitly in the configuration (inline mode). See +[Backend discovery modes](./backend-discovery.mdx) for details on both +approaches. + +## Configure 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. + +## Expose the service + +Choose how to expose the vMCP endpoint. The Service resource is created +automatically on port 4483. + +```yaml title="VirtualMCPServer resource" +spec: + 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 +``` + +**Service types:** + +- **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) + +The Service is named `vmcp-`, where `` is from `metadata.name` in +the VirtualMCPServer resource. + +## Monitor status + +Check the VirtualMCPServer status to verify it's ready: + +```bash +kubectl get virtualmcpserver my-vmcp +``` + +Key status fields: + +| Field | Description | +| -------------------- | ------------------------------------------------ | +| `phase` | Current state (Pending, Ready, Degraded, Failed) | +| `url` | Service URL for client connections | +| `backendCount` | Number of discovered backend MCP servers | +| `discoveredBackends` | Details about each backend and its auth type | + +## Operational configuration + +### Health checks + +vMCP continuously monitors backend health to detect failures and route requests +appropriately. Health check behavior is configurable via the VirtualMCPServer +resource. + +#### Health check configuration + +Configure health monitoring in `spec.config.operational.failureHandling`: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp + namespace: toolhive-system +spec: + config: + groupRef: my-group + operational: + failureHandling: + # Health check interval (how often to check each backend) + # Default: 30s + healthCheckInterval: 30s + + # Health check timeout (max duration for a single check) + # Should be less than healthCheckInterval + # Default: 10s + healthCheckTimeout: 10s + + # Number of consecutive failures before marking unhealthy + # Default: 3 + unhealthyThreshold: 3 + + # How often to report status updates to Kubernetes + # Default: 30s + statusReportingInterval: 30s + incomingAuth: + type: anonymous +``` + +#### Circuit breaker configuration + +Circuit breakers prevent cascading failures by temporarily stopping requests to +consistently failing backends. For detailed configuration, behavior, and +troubleshooting, see [Failure handling](./failure-handling.mdx). + +To enable circuit breaker: + +```yaml +spec: + config: + operational: + failureHandling: + circuitBreaker: + enabled: true + failureThreshold: 5 # Number of failures before opening circuit + timeout: 60s # How long to wait before attempting recovery +``` + +### Timeouts + +Configure timeouts for backend requests: + +```yaml +spec: + config: + operational: + timeouts: + # Default timeout for all backend requests (default: 30s) + default: 30s + + # Per-workload timeout overrides + perWorkload: + slow-backend: 60s + fast-backend: 10s +``` + +:::note + +Health check timeouts are configured separately via +`failureHandling.healthCheckTimeout` (default: 10s), not via the `timeouts` +section. + +::: + +#### Remote workload health checks + +By default, health checks are: + +- **Always enabled** for local backends (MCPServer) +- **Disabled by default** for remote backends (MCPRemoteProxy) + +To enable health checks for remote workloads, set the +`TOOLHIVE_REMOTE_HEALTHCHECKS` environment variable in the vMCP pod: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp +spec: + podTemplateSpec: + spec: + containers: + - name: vmcp + env: + - name: TOOLHIVE_REMOTE_HEALTHCHECKS + value: 'true' +``` + +For detailed backend health monitoring, see +[Verify backend status](./backend-discovery.mdx#verify-backend-status) in the +Backend discovery guide. + +## Next steps + +- [Configure backend discovery](./backend-discovery.mdx) to control how vMCP + finds and connects to MCP servers +- [Set up authentication](./authentication.mdx) for client-to-vMCP and + vMCP-to-backend security +- [Configure failure handling](./failure-handling.mdx) for circuit breakers and + partial failure modes +- [Monitor vMCP activity](./telemetry-and-metrics.mdx) with OpenTelemetry + tracing and metrics + +## Related information + +- [VirtualMCPServer CRD specification](../reference/crd-spec.md#apiv1alpha1virtualmcpserver) +- [Introduction to vMCP](./intro.mdx) +- [Scaling and Performance](./scaling-and-performance.mdx) +- [Backend discovery modes](./backend-discovery.mdx) +- [Tool aggregation](./tool-aggregation.mdx) +- [Optimize tool discovery](./optimizer.mdx) +- [Composite tools](./composite-tools.mdx) +- [Authentication](./authentication.mdx) +- [Proxy remote MCP servers](../guides-k8s/remote-mcp-proxy.mdx) diff --git a/versioned_docs/version-1.1/toolhive/guides-vmcp/failure-handling.mdx b/versioned_docs/version-1.1/toolhive/guides-vmcp/failure-handling.mdx new file mode 100644 index 00000000..9cdc4fd2 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-vmcp/failure-handling.mdx @@ -0,0 +1,423 @@ +--- +title: Failure handling +description: + Configure circuit breaker and partial failure modes to handle backend failures + gracefully. +--- + +Virtual MCP Server (vMCP) implements failure handling patterns to prevent +cascading failures and provide graceful degradation when backends become +unavailable. This guide covers circuit breaker configuration and partial failure +modes. + +:::tip + +For backend health status monitoring and the `/status` endpoint, see +[Backend discovery modes](./backend-discovery.mdx#verify-backend-status). + +::: + +## Overview + +When backends fail due to crashes, network issues, or rate limiting, vMCP +provides circuit breaker and partial failure modes to handle failures +gracefully: + +- **Circuit breaker**: Prevents cascading failures by immediately rejecting + requests to failing backends instead of waiting for timeouts +- **Partial failure modes**: Choose whether to fail entire requests or continue + with available backends +- **Automatic recovery**: Backends are automatically restored when they recover + +:::tip + +Enable circuit breaker for production environments where backends may experience +temporary failures (deployments, restarts, rate limits). For highly stable +backends, health checks alone may be sufficient. + +::: + +## Circuit breaker + +The circuit breaker tracks backend failures and transitions through three +states: + +1. **Closed** (normal operation): Requests pass through to the backend. Failures + are counted. +2. **Open** (failing state): After exceeding the failure threshold, the circuit + opens. Requests fail immediately without contacting the backend. +3. **Half-open** (recovery testing): After a timeout period, the circuit allows + exactly one test request through. While this request is in progress, all + other requests are rejected (circuit remains half-open). If the test + succeeds, the circuit closes immediately and normal operation resumes. If it + fails, the circuit reopens for another timeout period. + +```mermaid +stateDiagram-v2 + [*] --> Closed + Closed --> Open: Failure threshold exceeded + Open --> HalfOpen: Timeout elapsed + HalfOpen --> Closed: Request succeeds + HalfOpen --> Open: Request fails + Closed --> Closed: Success (reset count) +``` + +### Enable circuit breaker + +Configure circuit breaker in the VirtualMCPServer resource: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp + namespace: toolhive-system +spec: + config: + groupRef: my-group + # highlight-start + operational: + failureHandling: + healthCheckInterval: 30s + unhealthyThreshold: 3 + circuitBreaker: + enabled: true + failureThreshold: 5 + timeout: 60s + # highlight-end + incomingAuth: + type: anonymous +``` + +### Configuration options + +| Field | Description | Default | +| ------------------------- | ----------------------------------------------------- | ------- | +| `healthCheckInterval` | Time between health checks for each backend | `30s` | +| `unhealthyThreshold` | Consecutive failures before marking backend unhealthy | `3` | +| `healthCheckTimeout` | Maximum duration for a single health check | `10s` | +| `statusReportingInterval` | Interval for reporting status to Kubernetes | `30s` | +| **Circuit breaker** | | | +| `enabled` | Enable circuit breaker | `false` | +| `failureThreshold` | Number of failures before opening the circuit | `5` | +| `timeout` | Duration to wait before testing recovery | `60s` | + +:::note + +Circuit breaker is disabled by default. Health checks run independently of the +circuit breaker and mark backends as healthy/unhealthy based on +`unhealthyThreshold`. + +::: + +:::note[Two failure thresholds] + +vMCP uses two thresholds: + +- **`unhealthyThreshold`** (default: 3): Consecutive health check failures + before marking backend unhealthy +- **`failureThreshold`** (default: 5): Consecutive request failures before + opening circuit breaker + +Health checks detect failures during idle periods (max detection time: +`healthCheckInterval × unhealthyThreshold`). Circuit breaker provides fast +failure protection during active traffic. + +::: + +## Partial failure modes + +Configure how vMCP behaves when some backends are unavailable: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp + namespace: toolhive-system +spec: + config: + groupRef: my-group + operational: + failureHandling: + # highlight-next-line + partialFailureMode: best_effort + incomingAuth: + type: anonymous +``` + +### Modes + +- **`fail`** (default): Entire request fails if any required backend is + unavailable. Use when all backends must be operational. +- **`best_effort`**: Return results from healthy backends even if some fail. + Tools from failed backends are omitted from responses. Use for graceful + degradation. + +### Example: Best effort mode + +With `partialFailureMode: best_effort`, if the GitHub backend is down but Fetch +is healthy, the `tools/list` response only includes tools from healthy backends: + +```json +{ + "jsonrpc": "2.0", + "result": { + "tools": [{ "name": "fetch_url", "description": "Fetch URL content" }] + }, + "id": 1 +} +``` + +GitHub tools are omitted from the response because the circuit breaker is open. +The client doesn't see unavailable backend tools, preventing timeout errors when +attempting to call them. + +## Monitor circuit breaker status + +Check backend health and circuit state: + +```bash +kubectl get virtualmcpserver my-vmcp -n toolhive-system -o yaml +``` + +Status includes health information and circuit breaker state: + +```yaml +status: + phase: Degraded # Ready|Degraded if some backends unhealthy + backendCount: 2 # Only counts ready backends (fetch-mcp, jira-mcp) + discoveredBackends: + - name: github-mcp + status: unavailable + lastHealthCheck: '2025-02-09T10:29:45Z' + message: 'connection timeout' + circuitBreakerState: open # Circuit breaker state: closed|open|half-open + circuitLastChanged: '2025-02-09T10:28:30Z' # When circuit opened + consecutiveFailures: 8 # Current failure count + - name: fetch-mcp + status: ready + lastHealthCheck: '2025-02-09T10:30:05Z' + circuitBreakerState: closed + consecutiveFailures: 0 + - name: jira-mcp + status: ready + lastHealthCheck: '2025-02-09T10:30:03Z' + circuitBreakerState: half-open # Testing recovery + circuitLastChanged: '2025-02-09T10:30:00Z' + consecutiveFailures: 2 # Reduced after partial recovery +``` + +**Status fields:** + +- `status`: Backend health (ready, degraded, unavailable, unknown) +- `circuitBreakerState`: Circuit state (closed, open, half-open) - empty if + circuit breaker disabled +- `circuitLastChanged`: When the circuit breaker state last changed +- `consecutiveFailures`: Count of consecutive health check failures +- `message`: Additional information about backend status or errors + +The `/status` HTTP endpoint provides a simplified view: + +```bash +curl http://localhost:4483/status +``` + +```json +{ + "backends": [ + { + "name": "github-mcp", + "health": "unhealthy", + "transport": "sse", + "auth_type": "token_exchange" + }, + { + "name": "fetch-mcp", + "health": "healthy", + "transport": "streamable-http", + "auth_type": "unauthenticated" + } + ], + "healthy": false, + "version": "v1.2.3", + "group_ref": "my-group" +} +``` + +:::info + +The `/status` endpoint provides basic health information but does not include +circuit breaker state. For detailed circuit breaker information +(`circuitBreakerState`, `consecutiveFailures`, `circuitLastChanged`), use the +Kubernetes status shown above. See +[Backend discovery modes](./backend-discovery.mdx#verify-backend-status) for +more details on the `/status` endpoint. + +::: + +## Example configurations + +### Production with aggressive failure detection + +Detect failures quickly and fail fast: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: production-vmcp + namespace: toolhive-system +spec: + config: + groupRef: production-backends + operational: + failureHandling: + # Check every 10 seconds + healthCheckInterval: 10s + # Mark unhealthy after 2 failures (20 seconds) + unhealthyThreshold: 2 + healthCheckTimeout: 5s + # Open circuit after 3 failures + circuitBreaker: + enabled: true + failureThreshold: 3 + timeout: 30s + # Fail requests if any backend down + partialFailureMode: fail + incomingAuth: + type: oidc + oidc: + issuerRef: + name: my-issuer +``` + +### Development with best effort + +Continue with available backends: + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: dev-vmcp + namespace: toolhive-system +spec: + config: + groupRef: dev-backends + operational: + failureHandling: + healthCheckInterval: 30s + unhealthyThreshold: 3 + circuitBreaker: + enabled: true + failureThreshold: 5 + timeout: 60s + # Continue with healthy backends + partialFailureMode: best_effort + incomingAuth: + type: anonymous +``` + +## Troubleshooting + +
+Circuit breaker opens too frequently + +If the circuit breaker is too sensitive: + +**Increase failure threshold:** + +```yaml +operational: + failureHandling: + circuitBreaker: + failureThreshold: 10 # Require more failures before opening +``` + +**Increase timeout:** + +```yaml +operational: + failureHandling: + circuitBreaker: + timeout: 120s # Give backends more time to recover +``` + +
+ +
+Backends not recovering automatically + +If backends stay unhealthy after recovering: + +1. **Test backend connectivity** + + Verify the backend MCP server is accessible from vMCP: + + ```bash + kubectl exec -n toolhive-system deployment/vmcp-my-vmcp -- \ + curl -v http://my-backend:8080/mcp + ``` + + The backend should respond with MCP protocol headers. + +2. **Increase circuit breaker timeout** + + ```yaml + operational: + failureHandling: + circuitBreaker: + timeout: 90s # Allow more time for full recovery + ``` + +3. **Review vMCP logs** + + ```bash + kubectl logs -n toolhive-system deployment/vmcp-my-vmcp + ``` + + Look for circuit breaker state transitions: + + ``` + WARN Circuit breaker for backend github-mcp OPENED (threshold exceeded) + INFO Circuit breaker for backend github-mcp CLOSED (recovery successful) + ``` + +
+ +
+Healthy backends marked unhealthy + +If backends are incorrectly marked unhealthy: + +**Increase health check timeout:** + +```yaml +operational: + failureHandling: + healthCheckTimeout: 20s # Allow slower responses +``` + +**Increase unhealthy threshold:** + +```yaml +operational: + failureHandling: + unhealthyThreshold: 5 # Allow more failures before marking unhealthy +``` + +
+ +## Next steps + +- [Monitor vMCP activity](./telemetry-and-metrics.mdx) with OpenTelemetry + tracing and metrics +- [Set up audit logging](./audit-logging.mdx) to track requests and failures + +## Related information + +- [Backend discovery modes](./backend-discovery.mdx) - backend health status and + `/status` endpoint +- [Configuration guide](./configuration.mdx) +- [VirtualMCPServer CRD specification](../reference/crd-spec.md#apiv1alpha1virtualmcpserver) diff --git a/versioned_docs/version-1.1/toolhive/guides-vmcp/index.mdx b/versioned_docs/version-1.1/toolhive/guides-vmcp/index.mdx new file mode 100644 index 00000000..bee2709b --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-vmcp/index.mdx @@ -0,0 +1,27 @@ +--- +title: Virtual MCP Server +description: Aggregate multiple MCP servers into a single unified endpoint. +--- + +import DocCardList from '@theme/DocCardList'; + +## Introduction + +Virtual MCP Server (vMCP) is ToolHive's MCP gateway. It aggregates multiple +backend MCP servers into a single endpoint, giving clients unified tool access, +centralized authentication, and multi-step workflows — all through one +connection. + +## Where to start + +- **Evaluating vMCP?** Read + [Understanding Virtual MCP Server](../concepts/vmcp.mdx) for the full picture + of what it does and when it's the right fit. +- **Ready to try it?** Follow the [Quickstart](./quickstart.mdx) to deploy your + first vMCP on a Kubernetes cluster. +- **Already running vMCP?** Jump to [Configuration](./configuration.mdx) or + [Authentication](./authentication.mdx). + +## Contents + + diff --git a/versioned_docs/version-1.1/toolhive/guides-vmcp/intro.mdx b/versioned_docs/version-1.1/toolhive/guides-vmcp/intro.mdx new file mode 100644 index 00000000..f1454054 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-vmcp/intro.mdx @@ -0,0 +1,119 @@ +--- +title: Introduction to vMCP +description: Understand what Virtual MCP Server (vMCP) does and when to use it. +--- + +## What is vMCP? + +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. + +vMCP supports two types of backends: + +- **MCPServer**: Container-based MCP servers running in your cluster +- **MCPRemoteProxy**: Proxies to external remote MCP servers (such as Notion, + analytics platforms, or other SaaS MCP endpoints) + +:::note[MCPRemoteProxy support] + +vMCP can discover MCPRemoteProxy backends in a group, but authentication between +vMCP and MCPRemoteProxy is not yet fully implemented. MCPServer backends work +fully with vMCP. See +[Proxy remote MCP servers](../guides-k8s/remote-mcp-proxy.mdx#use-with-virtual-mcp-server) +for details on the current limitations. + +::: + +## Core capabilities + +- **Multi-server aggregation**: Connect to one endpoint instead of many, + including both local container-based servers and remote MCP proxies +- **Tool conflict resolution**: Automatic namespacing when backend MCP servers + have overlapping tool names +- **Centralized authentication**: Single sign-on with per-backend token exchange +- **Composite workflows**: Multi-step operations across backend MCP servers with + parallel execution, approval gates, and error handling +- **Tool optimization**: Replace all individual tool definitions with two + lightweight primitives (`find_tool` and `call_tool`) to reduce token usage and + improve tool selection. See [Optimize tool discovery](./optimizer.mdx) and the + underlying [concepts](../concepts/tool-optimization.mdx) + +## When to use vMCP + +### Good fit + +- You manage 5+ MCP servers (local or remote) +- You need cross-system workflows requiring coordination +- You have centralized authentication and authorization requirements +- You need reusable workflow definitions +- You want to aggregate external SaaS MCP servers with internal tools +- You want to reduce token usage and improve tool selection accuracy across many + backends with the [optimizer](./optimizer.mdx) + +### Not needed + +- You use a single MCP server +- You have simple, one-step operations +- You have no orchestration requirements + +## Architecture overview + +```mermaid +flowchart TB + Client[MCP Client] --> vMCP[Virtual MCP Server] + + subgraph LocalBackends[Local MCP Servers] + GitHub[GitHub MCP] + Fetch[Fetch MCP] + end + + subgraph RemoteBackends[Remote MCP Proxies] + Notion[Notion MCP] + Neon[Neon MCP] + end + + vMCP --> LocalBackends + vMCP --> RemoteBackends + + RemoteBackends -->|proxied| ExternalServices[External Services] +``` + +## How it works + +1. You define an MCPGroup (a resource that organizes related MCP servers) +2. Backend resources reference the group using `groupRef`: + - **MCPServer** resources for container-based MCP servers + - **MCPRemoteProxy** resources for external remote MCP servers +3. You create a VirtualMCPServer that references the group +4. The operator discovers all MCPServer and MCPRemoteProxy backends in the group + and aggregates their capabilities +5. Clients connect to the VirtualMCPServer endpoint and see a unified view of + all tools from both local and remote backends + +## Optimize tool discovery + +As the number of aggregated backends grows, clients receive a large number of +tool definitions that consume tokens and can degrade tool selection accuracy. +The vMCP optimizer addresses this by replacing all individual tool definitions +with two lightweight primitives (`find_tool` and `call_tool`) and using hybrid +semantic and keyword search to surface only the most relevant tools per request. +To enable the optimizer, add an `embeddingServerRef` to your VirtualMCPServer +resource. See [Optimize tool discovery](./optimizer.mdx) for the full setup +guide. + +## Next steps + +- [Try the Quickstart](./quickstart.mdx) to deploy your first vMCP on a + Kubernetes cluster +- [Configure vMCP servers](./configuration.mdx) to set up groups, backends, and + tool aggregation + +## Related information + +- [Understanding Virtual MCP Server](../concepts/vmcp.mdx) +- [Optimize tool discovery](./optimizer.mdx) +- [Scaling and performance](./scaling-and-performance.mdx) +- [Proxy remote MCP servers](../guides-k8s/remote-mcp-proxy.mdx) diff --git a/versioned_docs/version-1.1/toolhive/guides-vmcp/optimizer.mdx b/versioned_docs/version-1.1/toolhive/guides-vmcp/optimizer.mdx new file mode 100644 index 00000000..43a99a48 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-vmcp/optimizer.mdx @@ -0,0 +1,324 @@ +--- +title: Optimize tool discovery +description: + Enable the optimizer in vMCP to reduce token usage and improve tool selection + across aggregated backends. +--- + +When Virtual MCP Server (vMCP) aggregates many backend MCP servers, the total +number of tools exposed to clients can grow quickly. The optimizer addresses +this by filtering tools per request, reducing token usage and improving tool +selection accuracy. + +For the desktop/CLI approach using the MCP Optimizer container, see the +[MCP Optimizer tutorial](../tutorials/mcp-optimizer.mdx). This guide covers the +Kubernetes operator approach using VirtualMCPServer and EmbeddingServer CRDs. + +## Benefits + +- **Reduced token usage**: Only relevant tools are included in context, not the + entire toolset +- **Improved tool selection**: The right tools surface for each query. With + fewer tools to reason over, agents are more likely to choose correctly + +## How it works + +1. You send a prompt that requires tool assistance +2. The AI calls `find_tool` with keywords extracted from the prompt +3. vMCP performs hybrid semantic and keyword search across all backend tools +4. Only the most relevant tools (up to 8 by default) are returned +5. The AI calls `call_tool` to execute the selected tool, and vMCP routes the + request to the appropriate backend + +```mermaid +flowchart TB + subgraph vmcpGroup["VirtualMCPServer"] + direction TB + vmcp["vMCP (optimizer enabled)"] + end + subgraph embedding["EmbeddingServer"] + direction TB + tei["Text Embeddings Inference"] + end + subgraph backends["MCPGroup backends"] + direction TB + mcp1["MCP server"] + mcp2["MCP server"] + mcp3["MCP server"] + end + + client(["Client"]) <-- "find_tool / call_tool" --> vmcpGroup + vmcp <-. "semantic search" .-> embedding + vmcp <-. "discovers / routes" .-> backends +``` + +:::info[How search works internally] + +The optimizer uses an internal SQLite database for both keyword search (using +full-text search) and storing semantic vectors. Keyword search runs locally +against this database; semantic search uses vectors generated by an embedding +server. You can control how results from these two sources are blended — see the +[parameter reference](#parameter-reference) for details. + +::: + +## Quick start + +### Step 1: Create an EmbeddingServer + +Create an EmbeddingServer with default settings. This deploys a text embeddings +inference (TEI) server using the `BAAI/bge-small-en-v1.5` model: + +```yaml title="embedding-server.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: EmbeddingServer +metadata: + name: my-embedding + namespace: toolhive-system +spec: {} +``` + +:::tip + +Wait for the EmbeddingServer to reach the `Running` phase before proceeding. The +first startup may take a few minutes while the model downloads. + +```bash +kubectl get embeddingserver my-embedding -n toolhive-system -w +``` + +::: + +### Step 2: Add the embedding reference to VirtualMCPServer + +Update your existing VirtualMCPServer to include `embeddingServerRef`. **This is +the only change needed to enable the optimizer.** When you set +`embeddingServerRef`, the operator automatically enables the optimizer with +sensible defaults. You only need to add an explicit `optimizer` block if you +want to [tune the parameters](#tune-the-optimizer). + +```yaml title="VirtualMCPServer resource" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp + namespace: toolhive-system +spec: + # highlight-start + embeddingServerRef: + name: my-embedding + # highlight-end + config: + groupRef: my-group + incomingAuth: + type: anonymous +``` + +### Step 3: Verify + +Check that the VirtualMCPServer is ready: + +```bash +kubectl get virtualmcpserver my-vmcp -n toolhive-system +``` + +Look for `READY: True` in the output. Once ready, clients connecting to the vMCP +endpoint see only `find_tool` and `call_tool` instead of the full backend +toolset. + +## EmbeddingServer resource + +The EmbeddingServer CRD manages the lifecycle of a TEI server. An empty +`spec: {}` uses all defaults. The two most important fields you can customize +are: + +- **`model`**: The Hugging Face embedding model to use. The default + (`BAAI/bge-small-en-v1.5`) is the tested and recommended model. You can + substitute any embedding model available on Hugging Face — see the + [MTEB leaderboard](https://huggingface.co/spaces/mteb/leaderboard) to compare + options. +- **`image`**: The container image for + [text-embeddings-inference](https://github.com/huggingface/text-embeddings-inference) + (TEI). The default is the CPU-only image + (`ghcr.io/huggingface/text-embeddings-inference:cpu-latest`). Swap this for a + CUDA-enabled image if you have GPU nodes available. + +For the complete field reference, see the +[EmbeddingServer CRD specification](../reference/crd-spec.md#apiv1alpha1embeddingserver). + +:::warning[ARM64 compatibility] + +The default TEI CPU images depend on Intel MKL, which is x86_64-only. No +official ARM64 images exist yet. On ARM64 nodes (including Apple Silicon with +kind), you can run the amd64 image under emulation as a workaround. + +First, pull the amd64 image and load it into your cluster: + +```bash +docker pull --platform linux/amd64 \ + ghcr.io/huggingface/text-embeddings-inference:cpu-1.7 +kind load docker-image \ + ghcr.io/huggingface/text-embeddings-inference:cpu-1.7 +``` + +The `kind load` command is specific to kind. For other cluster distributions, +use the equivalent image-loading mechanism (for example, `ctr images import` for +containerd, or push the image to a registry your cluster can pull from). + +Then, pin the image in your EmbeddingServer so the operator uses the pre-pulled +tag instead of the default `cpu-latest`: + +```yaml title="embedding-server.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: EmbeddingServer +metadata: + name: my-embedding + namespace: toolhive-system +spec: + image: ghcr.io/huggingface/text-embeddings-inference:cpu-1.7 +``` + +Native ARM64 support is in progress upstream. Track the +[TEI GitHub repository](https://github.com/huggingface/text-embeddings-inference) +for updates. + +::: + +## Tune the optimizer + +To customize optimizer behavior, add the `optimizer` block under `spec.config` +in your VirtualMCPServer resource: + +```yaml title="VirtualMCPServer resource" +spec: + config: + groupRef: my-group + # highlight-start + optimizer: + embeddingServiceTimeout: 30s + maxToolsToReturn: 8 + hybridSearchSemanticRatio: '0.5' + semanticDistanceThreshold: '1.0' + # highlight-end +``` + +### Parameter reference + +| Parameter | Description | Default | +| --------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | +| `embeddingServiceTimeout` | HTTP request timeout for calls to the embedding service | `30s` | +| `maxToolsToReturn` | Maximum number of tools returned per search (1-50) | `8` | +| `hybridSearchSemanticRatio` | Balance between semantic and keyword search. `0.0` = all keyword, `1.0` = all semantic. Default gives equal weight to both. | `"0.5"` | +| `semanticDistanceThreshold` | Maximum distance from the search term for semantic results. `0` = identical, `2` = completely unrelated. Results beyond this threshold are filtered out. | `"1.0"` | + +:::note + +`hybridSearchSemanticRatio` and `semanticDistanceThreshold` are string-encoded +floats (for example, `"0.5"` not `0.5`). This is a Kubernetes CRD limitation, as +CRDs do not support float types portably. + +::: + +:::info[EmbeddingServer is always required] + +Even if you set `hybridSearchSemanticRatio` to `"0.0"` (all keyword search), the +optimizer still requires a configured EmbeddingServer. The EmbeddingServer won't +be used at runtime when the semantic ratio is `0.0`, but the configuration must +be present due to how the optimizer is wired internally. + +::: + +:::tip[Tuning guidance] + +The defaults are well-tested and work for most use cases. If you do need to +adjust them: + +- **Lower `semanticDistanceThreshold`** (for example, `"0.6"`) for higher + precision: only very close matches are returned +- **Raise `semanticDistanceThreshold`** (for example, `"1.4"`) for higher + recall: broader matches are included +- **Increase `maxToolsToReturn`** if the AI frequently cannot find the right + tool; decrease it to save tokens +- **Adjust `hybridSearchSemanticRatio`** toward `"1.0"` if tool names are not + descriptive, or toward `"0.0"` if exact keyword matching is more useful +- `semanticDistanceThreshold` filtering is applied before the `maxToolsToReturn` + cap. A low threshold can filter out candidates before the cap takes effect, so + you may need to raise the threshold if too few results are returned + +::: + +## Complete example + +This example shows a full configuration with all available options, including +high availability for the embedding server, persistent model caching, and tuned +optimizer parameters. + +The EmbeddingServer runs two replicas with resource limits and a persistent +volume for model caching, so restarts don't re-download the model: + +```yaml title="embedding-server-full.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: EmbeddingServer +metadata: + name: full-embedding + namespace: toolhive-system +spec: + replicas: 2 + resources: + requests: + cpu: '500m' + memory: '512Mi' + limits: + cpu: '2' + memory: '1Gi' + modelCache: + enabled: true + storageSize: 5Gi +``` + +The VirtualMCPServer uses a shorter embedding timeout (15s) because the +EmbeddingServer is co-located with low-latency access. Increase this value if +the embedding service is remote or under high load: + +```yaml title="vmcp-with-optimizer.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: full-vmcp + namespace: toolhive-system +spec: + embeddingServerRef: + name: full-embedding + config: + groupRef: my-tools + optimizer: + embeddingServiceTimeout: 15s + maxToolsToReturn: 10 + hybridSearchSemanticRatio: '0.6' + semanticDistanceThreshold: '0.8' + incomingAuth: + type: oidc + oidcConfig: + type: inline + inline: + issuer: https://auth.example.com + audience: vmcp-example +``` + +## Next steps + +- [Configure failure handling](./failure-handling.mdx) for circuit breakers and + partial failure modes +- [Monitor vMCP activity](./telemetry-and-metrics.mdx) with OpenTelemetry + tracing and metrics + +## Related information + +- [MCP Optimizer tutorial](../tutorials/mcp-optimizer.mdx) - desktop/CLI setup +- [Optimizing LLM context](../concepts/tool-optimization.mdx) - background on + tool filtering and context pollution +- [Configure vMCP servers](./configuration.mdx) +- [EmbeddingServer CRD specification](../reference/crd-spec.md#apiv1alpha1embeddingserver) +- [Virtual MCP Server overview](../concepts/vmcp.mdx) - conceptual overview of + vMCP +- [VirtualMCPServer CRD specification](../reference/crd-spec.md#apiv1alpha1virtualmcpserver) diff --git a/versioned_docs/version-1.1/toolhive/guides-vmcp/quickstart.mdx b/versioned_docs/version-1.1/toolhive/guides-vmcp/quickstart.mdx new file mode 100644 index 00000000..1f60db06 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-vmcp/quickstart.mdx @@ -0,0 +1,282 @@ +--- +title: 'Quickstart: Virtual MCP Server' +sidebar_label: Quickstart +description: + Learn how to aggregate multiple MCP servers into a single endpoint using + Virtual MCP Server. +--- + +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 + +- How to create an MCPGroup to organize backend servers +- How to deploy multiple MCPServers in a group +- How to create a VirtualMCPServer that aggregates them +- How tool conflict resolution works +- How to connect your AI client to the aggregated endpoint + +## Prerequisites + +Before starting this tutorial, make sure you have: + +- A Kubernetes cluster with the ToolHive operator installed (see + [Quickstart: Kubernetes Operator](../guides-k8s/quickstart.mdx)) +- `kubectl` configured to communicate with your cluster +- An MCP client (Visual Studio Code with Copilot is used in this tutorial) + +## Step 1: Create an MCPGroup + +First, create an MCPGroup to organize your backend MCP servers: + +```yaml title="mcpgroup.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPGroup +metadata: + name: demo-tools + namespace: toolhive-system +spec: + description: Demo group for vMCP aggregation +``` + +Apply the resource: + +```bash +kubectl apply -f mcpgroup.yaml +``` + +Verify the group was created: + +```bash +kubectl get mcpgroups -n toolhive-system +``` + +## Step 2: Deploy backend MCPServers + +Deploy two MCP servers that will be aggregated. Both reference the `demo-tools` +group in the `groupRef` field: + +```yaml {11,30} title="mcpservers.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: fetch + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/gofetch/server + transport: streamable-http + proxyPort: 8080 + mcpPort: 8080 + groupRef: demo-tools + resources: + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' +--- +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: osv + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/osv-mcp/server + transport: streamable-http + proxyPort: 8080 + mcpPort: 8080 + groupRef: demo-tools + resources: + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' +``` + +Apply the resources: + +```bash +kubectl apply -f mcpservers.yaml +``` + +Wait for both servers to be running: + +```bash +kubectl get mcpservers -n toolhive-system -w +``` + +You should see both servers with `Running` status before continuing. + +## Step 3: Create a VirtualMCPServer + +Create a VirtualMCPServer that aggregates both backends: + +```yaml title="virtualmcpserver.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: demo-vmcp + namespace: toolhive-system +spec: + # No incoming auth for development (anonymous access) + incomingAuth: + type: anonymous + + # Auto-discover auth config from backend MCPServers + outgoingAuth: + source: inline + # No default specified will use anonymous + + # Expose as ClusterIP (cluster-internal or exposed via Ingress/Gateway) + serviceType: ClusterIP + + config: + # Reference the MCPGroup containing fetch and osv servers + groupRef: demo-tools + # Tool aggregation with prefix strategy to avoid naming conflicts + aggregation: + conflictResolution: prefix + conflictResolutionConfig: + prefixFormat: '{workload}_' +``` + +Apply the resource: + +```bash +kubectl apply -f virtualmcpserver.yaml +``` + +Check the status: + +```bash +kubectl get virtualmcpservers -n toolhive-system +``` + +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 vMCP to +aggregate their tools. With the `prefix` conflict resolution strategy, all tools +are prefixed with the backend name. + +::: + +## Step 4: Verify the aggregation + +Check the discovered backends: + +```bash +kubectl describe virtualmcpserver demo-vmcp -n toolhive-system +``` + +Look for the `Discovered Backends` section in the status, which should show both +backends. + +## Step 5: Connect your client + +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 +``` + +Test the health endpoint: + +```bash +curl http://localhost:4483/health +``` + +You should see `{"status":"ok"}`. + +Add the port-forwarded vMCP endpoint as a remote server in ToolHive: + +```bash +thv run http://localhost:4483/mcp --name demo-vmcp +``` + +This registers the vMCP endpoint as a ToolHive-managed workload, which +automatically configures your registered MCP clients to connect to it. + +:::tip + +If you haven't set up client configuration yet, run `thv client setup` to +register your MCP clients. See +[Client configuration](../guides-cli/client-configuration.mdx) for more details. + +::: + +## Step 6: Test the aggregated tools + +Try asking your AI assistant questions that use the aggregated tools. Both tools +work through the same vMCP endpoint! + +## Step 7: Clean up + +Delete the resources when you're done: + +```bash +kubectl delete virtualmcpserver demo-vmcp -n toolhive-system +kubectl delete mcpserver fetch osv -n toolhive-system +kubectl delete mcpgroup demo-tools -n toolhive-system +``` + +## What's next? + +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.mdx) + +## Troubleshooting + +
+VirtualMCPServer stuck in Pending + +Check that the MCPGroup exists and backend MCPServers are running: + +```bash +kubectl get mcpgroups,mcpservers -n toolhive-system +``` + +Check the operator logs: + +```bash +kubectl logs -n toolhive-system -l app.kubernetes.io/name=toolhive-operator +``` + +
+ +
+Only some tools appearing + +Verify both backends are discovered: + +```bash +kubectl get virtualmcpserver demo-vmcp -n toolhive-system -o jsonpath='{.status.discoveredBackends[*].name}' +``` + +Check backend health in the status: + +```bash +kubectl describe virtualmcpserver demo-vmcp -n toolhive-system +``` + +
diff --git a/versioned_docs/version-1.1/toolhive/guides-vmcp/scaling-and-performance.mdx b/versioned_docs/version-1.1/toolhive/guides-vmcp/scaling-and-performance.mdx new file mode 100644 index 00000000..ed2ca244 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-vmcp/scaling-and-performance.mdx @@ -0,0 +1,95 @@ +--- +title: Scaling and Performance +description: How to scale Virtual MCP Server deployments vertically and horizontally. +--- + +This guide explains how to scale Virtual MCP Server (vMCP) deployments. + +## Vertical scaling + +Vertical scaling (increasing CPU/memory per instance) is the simplest approach +and works for all use cases, including stateful backends. + +To increase resources, configure `podTemplateSpec` in your VirtualMCPServer: + +```yaml +spec: + podTemplateSpec: + spec: + containers: + - name: vmcp + resources: + requests: + cpu: '500m' + memory: 512Mi + limits: + cpu: '1' + memory: 1Gi +``` + +Vertical scaling is recommended as the starting point for most deployments. + +## Horizontal scaling + +Horizontal scaling (adding more replicas) can improve availability and handle +higher request volumes. + +### How to scale horizontally + +The VirtualMCPServer CRD does not have a `replicas` field. The operator creates +a Deployment named `vmcp-` (where `` is your VirtualMCPServer name) +with 1 replica and preserves the replicas count, allowing you to manage scaling +separately. + +**Option 1: Manual scaling** + +```bash +kubectl scale deployment vmcp- -n --replicas=3 +``` + +**Option 2: Autoscaling with HPA** + +```bash +kubectl autoscale deployment vmcp- -n \ + --min=2 --max=5 --cpu-percent=70 +``` + +### When horizontal scaling is challenging + +Horizontal scaling works well for **stateless backends** (fetch, search, +read-only operations) where sessions can be resumed on any instance. + +However, **stateful backends** make horizontal scaling difficult: + +- **Stateful backends** (Playwright browser sessions, database connections, file + system operations) require requests to be routed to the same vMCP instance + that established the session +- Session resumption may not work reliably for stateful backends + +The `VirtualMCPServer` CRD includes a `sessionAffinity` field that controls how +the Kubernetes Service routes repeated client connections. By default, it uses +`ClientIP` affinity, which routes connections from the same client IP to the +same pod. You can configure this using the `sessionAffinity` field: + +```yaml +spec: + sessionAffinity: ClientIP # default +``` + +For stateful backends, vertical scaling or dedicated vMCP instances per team/use +case are recommended instead of horizontal scaling. + +## Next steps + +- [Explore Kubernetes operator guides](../guides-k8s/index.mdx) for managing MCP + servers alongside vMCP +- [Curate a server catalog](../guides-registry/index.mdx) for your team with the + Registry Server + +## Related information + +- [Introduction to vMCP](./intro.mdx) +- [Configure health checks](./configuration.mdx#health-checks) +- [Backend discovery modes](./backend-discovery.mdx) +- [Telemetry and metrics](./telemetry-and-metrics.mdx) +- [VirtualMCPServer CRD specification](../reference/crd-spec.md#apiv1alpha1virtualmcpserver) diff --git a/versioned_docs/version-1.1/toolhive/guides-vmcp/telemetry-and-metrics.mdx b/versioned_docs/version-1.1/toolhive/guides-vmcp/telemetry-and-metrics.mdx new file mode 100644 index 00000000..10c0fbd7 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-vmcp/telemetry-and-metrics.mdx @@ -0,0 +1,181 @@ +--- +title: Telemetry and metrics +description: How to enable OpenTelemetry traces and metrics for Virtual MCP Server. +--- + +Virtual MCP Server (vMCP) provides comprehensive observability through +OpenTelemetry instrumentation. You can export traces and metrics to monitor +backend operations and workflow executions. + +## Telemetry types + +vMCP supports two types of telemetry: + +- **Traces**: Track requests across vMCP and its backends, showing the full path + of tool calls, resource reads, and workflow executions +- **Metrics**: Counters and histograms for backend request rates, error rates, + and latency distributions + +For general ToolHive observability concepts including trace structure and +metrics, see the [observability overview](../concepts/observability.mdx). + +## Enable telemetry + +Configure telemetry in the VirtualMCPServer resource using the +[`spec.config.telemetry` field](../reference/crd-spec.md#toolhivestacklokdevtelemetry): + +```yaml +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: my-vmcp + namespace: toolhive-system +spec: + config: + groupRef: my-group + # highlight-start + telemetry: + endpoint: 'otel-collector:4318' + serviceName: 'my-vmcp' + insecure: true + tracingEnabled: true + samplingRate: '0.05' + metricsEnabled: true + enablePrometheusMetricsPath: true + # highlight-end + incomingAuth: + type: anonymous +``` + +### Configuration options + +| Field | Description | Default | +| ----------------------------- | --------------------------------------- | --------------------- | +| `endpoint` | OTLP collector endpoint (hostname:port) | - | +| `serviceName` | Service name in traces and metrics | VirtualMCPServer name | +| `tracingEnabled` | Enable tracing | `false` | +| `metricsEnabled` | Enable OTLP metrics export | `false` | +| `samplingRate` | Trace sampling rate (0.0-1.0) | `"0.05"` | +| `insecure` | Use HTTP instead of HTTPS | `false` | +| `enablePrometheusMetricsPath` | Expose `/metrics` endpoint | `false` | + +## Export to observability backends + +### Export to Jaeger via OpenTelemetry Collector + +Deploy an OpenTelemetry Collector configured to export to Jaeger: + +```yaml title="otel-collector-config.yaml" +receivers: + otlp: + protocols: + http: + endpoint: 0.0.0.0:4318 + +processors: + batch: + timeout: 10s + send_batch_size: 1024 + +exporters: + otlp/jaeger: + endpoint: jaeger:4317 + tls: + insecure: true + +service: + pipelines: + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp/jaeger] +``` + +Then configure vMCP to send telemetry to the collector: + +```yaml +spec: + config: + telemetry: + endpoint: 'otel-collector:4318' + serviceName: 'production-vmcp' + tracingEnabled: true + metricsEnabled: true + insecure: true +``` + +## Metrics collection + +vMCP supports two methods for collecting metrics: + +- **Push via OpenTelemetry**: Set `metricsEnabled: true` to push metrics to your + OTel Collector via OTLP +- **Pull via Prometheus**: Set `enablePrometheusMetricsPath: true` to expose a + `/metrics` endpoint on the vMCP service port (4483) for Prometheus to scrape + +### Backend metrics + +These metrics track requests to individual MCP server backends: + +| Metric | Type | Description | +| ----------------------------------------- | --------- | ----------------------------------------------------------------------------- | +| `toolhive_vmcp_backends_discovered` | Gauge | Number of backends discovered | +| `toolhive_vmcp_backend_requests` | Counter | Total requests per backend | +| `toolhive_vmcp_backend_errors` | Counter | Total errors per backend | +| `toolhive_vmcp_backend_requests_duration` | Histogram | Duration of backend requests | +| `mcp.client.operation.duration` | Histogram | MCP client operation duration (`mcp_client_operation_duration` on `/metrics`) | + +### Workflow metrics + +These metrics track workflow execution across backends: + +| Metric | Type | Description | +| ----------------------------------- | --------- | ------------------------------- | +| `toolhive_vmcp_workflow_executions` | Counter | Total workflow executions | +| `toolhive_vmcp_workflow_errors` | Counter | Total workflow execution errors | +| `toolhive_vmcp_workflow_duration` | Histogram | Duration of workflow executions | + +### Optimizer metrics + +When the vMCP optimizer is enabled, these metrics track tool-finding and +tool-calling performance: + +| Metric | Type | Description | +| ----------------------------------------------- | --------- | --------------------------------------- | +| `toolhive_vmcp_optimizer_find_tool_requests` | Counter | Total FindTool calls | +| `toolhive_vmcp_optimizer_find_tool_errors` | Counter | Total FindTool errors | +| `toolhive_vmcp_optimizer_find_tool_duration` | Histogram | Duration of FindTool calls | +| `toolhive_vmcp_optimizer_find_tool_results` | Histogram | Number of tools returned per call | +| `toolhive_vmcp_optimizer_token_savings_percent` | Histogram | Token savings percentage per call | +| `toolhive_vmcp_optimizer_call_tool_requests` | Counter | Total CallTool calls | +| `toolhive_vmcp_optimizer_call_tool_errors` | Counter | Total CallTool errors | +| `toolhive_vmcp_optimizer_call_tool_not_found` | Counter | CallTool calls where tool was not found | +| `toolhive_vmcp_optimizer_call_tool_duration` | Histogram | Duration of CallTool calls | + +## Distributed tracing + +vMCP creates client-side spans for backend operations with the following span +names: + +- `tools/call ` - Tool calls to backends +- `resources/read` - Resource reads from backends +- `prompts/get ` - Prompt retrieval from backends +- `list_capabilities` - Backend capability discovery + +Each span includes attributes for the target backend (`target.workload_id`, +`target.workload_name`, `target.base_url`) and the relevant MCP attributes +(`mcp.method.name`, `gen_ai.tool.name`, `mcp.resource.uri`). + +## Next steps + +- [Set up audit logging](./audit-logging.mdx) for structured request and + authorization event tracking + +## Related information + +- [Observability concepts](../concepts/observability.mdx) - overview of + ToolHive's observability architecture +- [Kubernetes telemetry guide](../guides-k8s/telemetry-and-metrics.mdx) - + telemetry for MCPServer resources +- [OpenTelemetry tutorial](../integrations/opentelemetry.mdx) - set up a local + observability stack diff --git a/versioned_docs/version-1.1/toolhive/guides-vmcp/tool-aggregation.mdx b/versioned_docs/version-1.1/toolhive/guides-vmcp/tool-aggregation.mdx new file mode 100644 index 00000000..4044a2d4 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/guides-vmcp/tool-aggregation.mdx @@ -0,0 +1,250 @@ +--- +title: Tool aggregation and conflict resolution +description: + How vMCP aggregates tools from multiple backend MCP servers and resolves + naming conflicts. +--- + +When aggregating multiple MCP servers, tool name conflicts can occur when +different backend servers expose tools with the same name. Virtual MCP Server +(vMCP) provides strategies to resolve these conflicts automatically. + +## Overview + +vMCP discovers tools from all backend MCPServers in the referenced group and +presents them as a unified set to clients. When two backend MCP servers 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. + +:::tip + +When aggregating many backends, the total number of exposed tools can grow +quickly. Consider enabling the [optimizer](./optimizer.mdx) to reduce token +usage and improve tool selection accuracy. + +::: + +## Conflict resolution strategies + +### Prefix strategy (default) + +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 title="VirtualMCPServer resource" +spec: + config: + aggregation: + conflictResolution: prefix + conflictResolutionConfig: + prefixFormat: '{workload}_' +``` + +**Prefix format options:** + +| Format | Example result | +| ------------- | --------------------- | +| `{workload}` | `githubcreate_issue` | +| `{workload}_` | `github_create_issue` | +| `{workload}.` | `github.create_issue` | + +**Example:** + +With backend servers `github` and `jira`, both exposing `create_issue`: + +- GitHub's tool becomes `github_create_issue` +- Jira's tool becomes `jira_create_issue` + +### Priority strategy + +When multiple backend MCP servers 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 backend servers. + +```yaml title="VirtualMCPServer resource" +spec: + config: + aggregation: + conflictResolution: priority + conflictResolutionConfig: + 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 MCP server for specific tools +and want to hide duplicates. + +:::warning + +The priority strategy drops tools from lower-priority backend servers. Ensure +this is the intended behavior before using in production. + +::: + +### Manual strategy + +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 title="VirtualMCPServer resource" +spec: + config: + aggregation: + conflictResolution: manual + tools: + - workload: github + overrides: + create_issue: + name: gh_create_issue + - workload: jira + overrides: + create_issue: + name: jira_ticket +``` + +**When to use:** Production deployments where you want explicit control over +tool names. + +## Tool filtering + +Use filters to expose only specific tools from a backend MCP server, excluding +all others. This is useful for reducing the tool surface area presented to LLM +clients or removing unnecessary tools: + +```yaml title="VirtualMCPServer resource" +spec: + config: + aggregation: + tools: + - workload: github + filter: ['create_issue', 'list_issues', 'get_issue'] +``` + +Only the listed tools are included; all others from that backend MCP server are +excluded. + +## Tool overrides + +Use overrides to customize tool names and descriptions without modifying backend +MCP server configurations. This is useful for disambiguating similarly-named +tools or providing more context to LLM clients: + +```yaml title="VirtualMCPServer resource" +spec: + config: + aggregation: + tools: + - workload: github + overrides: + create_issue: + name: gh_new_issue + description: 'Create a new GitHub issue in the repository' +``` + +:::info + +You can also reference an `MCPToolConfig` resource using `toolConfigRef` instead +of inline filter and overrides. This feature is currently in development. + +::: + +## Combine filters and overrides + +You can combine filtering and overrides for fine-grained control: + +```yaml title="VirtualMCPServer resource" +spec: + config: + aggregation: + conflictResolution: prefix + conflictResolutionConfig: + prefixFormat: '{workload}_' + tools: + - workload: github + filter: ['create_issue', 'list_issues'] + overrides: + create_issue: + description: 'Create a GitHub issue (engineering team)' + - workload: jira + filter: ['create_issue', 'search_issues'] +``` + +## Example: Aggregating multiple MCP servers + +This example shows two MCP servers (fetch and osv) aggregated with prefix-based +conflict resolution: + +```yaml +# MCPGroup to organize backend servers +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPGroup +metadata: + name: demo-tools + namespace: toolhive-system +spec: + description: Demo group for tool aggregation +--- +# First backend: fetch server +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: fetch + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/gofetch/server + transport: streamable-http + proxyPort: 8080 + mcpPort: 8080 + groupRef: demo-tools +--- +# Second backend: osv server +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: osv + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/osv-mcp/server + transport: streamable-http + proxyPort: 8080 + mcpPort: 8080 + groupRef: demo-tools +--- +# VirtualMCPServer aggregating both backends +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: VirtualMCPServer +metadata: + name: demo-vmcp + namespace: toolhive-system +spec: + incomingAuth: + type: anonymous + config: + groupRef: demo-tools + aggregation: + conflictResolution: prefix + conflictResolutionConfig: + prefixFormat: '{workload}_' +``` + +With this configuration, tools from each backend are prefixed: + +- `fetch_*` tools from the fetch server +- `osv_*` tools from the osv server + +## Next steps + +- [Create composite tools](./composite-tools.mdx) to build multi-step workflows + that span multiple backends +- [Optimize tool discovery](./optimizer.mdx) to reduce token usage when + aggregating many tools + +## Related information + +- [VirtualMCPServer configuration reference](./configuration.mdx) +- [Customize MCP server tools](../guides-k8s/customize-tools.mdx) diff --git a/versioned_docs/version-1.1/toolhive/index.mdx b/versioned_docs/version-1.1/toolhive/index.mdx new file mode 100644 index 00000000..1d02bc63 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/index.mdx @@ -0,0 +1,244 @@ +--- +title: Introduction +hide_title: true +description: ToolHive helps you run and manage MCP servers easily and securely. +--- + +import useBaseUrl from '@docusaurus/useBaseUrl'; +import ThemedImage from '@theme/ThemedImage'; + + +
+ +--- + + + +# What is ToolHive? + +ToolHive is an enterprise-grade open source (Apache 2.0) platform for running +and managing Model Context Protocol (MCP) servers. + +New to the Model Context Protocol? Start with the +[MCP primer](./concepts/mcp-primer.mdx). + +## Choose your path + +
+
+ +### Desktop app + +Run MCP servers with one click. The easiest way to get started for individual +developers. + +**[Get started with the UI →](./guides-ui/quickstart.mdx)** + +
+
+ +### CLI + +For power users and automation. Full control over MCP servers from the command +line. + +**[Get started with the CLI →](./guides-cli/quickstart.mdx)** + +
+
+ +### Kubernetes + +For teams and enterprises. Deploy and manage MCP servers at scale with the +ToolHive Operator. + +**[Get started with K8s →](./guides-k8s/quickstart.mdx)** + +
+
+ +## ToolHive components + +ToolHive includes everything you need to use MCP servers in production. It's +made up of four key components: the Runtime, Registry Server, Gateway, and +Portal. + + + +### Runtime + +The ToolHive Runtime is the core component that runs MCP servers in isolated +containers. It provides a secure and scalable environment for deploying MCP +servers, with features like fine-grained permissions, network access controls, +and secret management. + +The ToolHive Runtime is available in three different editions to suit different +use cases: + +- [**ToolHive UI**](./guides-ui/index.mdx): A desktop application for individual + developers to run and manage MCP servers locally. It provides a user-friendly + interface to discover, deploy, and manage MCP servers. + +- [**ToolHive CLI**](./guides-cli/index.mdx): A command line interface to deploy + and manage MCP servers on your local machine or in development environments. + It allows quick deployment of MCP servers and supports advanced features like + agent skills, telemetry, and fine-grained authorization policies. + +- [**ToolHive Kubernetes Operator**](./guides-k8s/index.mdx): A Kubernetes + operator for teams and enterprises to run and manage MCP servers in multi-user + environments. It provides centralized management, security controls, and + integration with existing infrastructure. + +### Registry Server + +The [**ToolHive Registry Server**](./guides-registry/index.mdx) is an +implementation of the official +[MCP Registry API](https://github.com/modelcontextprotocol/registry/blob/main/docs/reference/api/generic-registry-api.md). +Curate a catalog of trusted servers from multiple backend sources for users to +quickly discover and deploy. It can be deployed standalone or as part of the +ToolHive Operator. + +### Gateway + +Implemented in the ToolHive Kubernetes Operator as the +[**Virtual MCP Server (vMCP)**](./guides-vmcp/index.mdx), the ToolHive Gateway +is a secure proxy for MCP server connectivity, aggregation, and orchestration. +It's available as part of the ToolHive Operator for Kubernetes deployments. + +### Portal + +The ToolHive Portal is how users discover and install MCP servers. It's +available as a cross-platform desktop app (the ToolHive UI) and an experimental +[web-based frontend](https://github.com/stacklok/toolhive-cloud-ui) to the +ToolHive Registry Server. + +### Better together + +ToolHive components work together to provide a seamless experience for both +admins and users: + +1. **Admins** curate and organize MCP servers in the **Registry**, configuring + access and policies. +2. **Users** discover and request MCP servers from the **Portal**, and ToolHive + orchestrates installation and access. +3. **Runtime** securely deploys and manages MCP servers across local and cloud + environments, integrating seamlessly with existing SDLC workflows, exporting + analytics, and enforcing fine-grained access control. +4. **Gateway** handles all inbound traffic, secures context and credentials, + optimizes tool selection, and applies organizational policies. + + + +## Why ToolHive? + +We want to help you get real value from MCP servers. While there are plenty of +tools to help you quickly build MCP servers, the obstacles to using those +servers effectively are largely operational. ToolHive addresses the runtime, +security, and other considerations necessary to use MCP servers with confidence +and in production. + +We address those considerations with proven, familiar technologies like +containers and Kubernetes. + +ToolHive lets you run any MCP server, regardless of its underlying technology +stack, even when the original authors didn't provide container images. We +containerize the MCP server and let you use a simple CLI or Kubernetes to manage +MCP deployments at any scale. Coordinate all your MCP servers from one place +with sensible security controls and container-native simplicity. + +## Key features + +ToolHive includes a range of capabilities to help you run and manage MCP servers +effectively: + +**Runtime:** + +- Run local MCP servers instantly from the built-in registry of vetted MCP + servers, any Docker container, or directly from package managers +- Deploy MCP servers in the cloud via Kubernetes for enterprise scalability +- Proxy remote MCP servers securely for unified management +- Apply fine-grained permissions and network access filtering +- Protect sensitive data with built-in secrets management and enterprise OAuth + integrations +- Leverage OpenTelemetry and Prometheus for observability and audit logging + +**Registry Server:** + +- Curate a catalog of trusted MCP servers from multiple sources: the official + MCP Registry, other public registries, and custom files +- Group servers based on role or use case +- Manage your registry with an API-driven interface +- Preset configurations and permissions for a frictionless user experience + +**Gateway:** + +- Orchestrate multiple tools with a deterministic workflow engine +- Centralize control of security policy, authentication, authorization, + auditing, etc. +- Customize and filter tools and descriptions to improve performance and reduce + token usage + +**Portal:** + +- Cross-platform [desktop app](https://github.com/stacklok/toolhive-studio) and + browser-based [cloud UI](https://github.com/stacklok/toolhive-cloud-ui) +- Make it easy for end users to discover and deploy MCP servers +- Install MCP servers with a single click +- Connect with local clients like Claude Desktop, Cursor, VS Code, and many more + +## Additional resources + +Join the Stacklok [Discord community](https://discord.gg/stacklok) to connect +with other ToolHive users, ask questions, and share your experiences. + +Source code and issue tracking: + +- ToolHive CLI & K8s Operator + - [GitHub repository](https://github.com/stacklok/toolhive) + - [Issue tracker](https://github.com/stacklok/toolhive/issues) +- ToolHive UI + - [GitHub repository](https://github.com/stacklok/toolhive-studio) + - [Issue tracker](https://github.com/stacklok/toolhive-studio/issues) +- ToolHive Registry Server + - [GitHub repository](https://github.com/stacklok/toolhive-registry-server) + - [Issue tracker](https://github.com/stacklok/toolhive-registry-server/issues) +- ToolHive Cloud UI (experimental) + - [GitHub repository](https://github.com/stacklok/toolhive-cloud-ui) + - [Issue tracker](https://github.com/stacklok/toolhive-cloud-ui/issues) diff --git a/versioned_docs/version-1.1/toolhive/integrations/aws-sts.mdx b/versioned_docs/version-1.1/toolhive/integrations/aws-sts.mdx new file mode 100644 index 00000000..41af94b2 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/integrations/aws-sts.mdx @@ -0,0 +1,663 @@ +--- +title: AWS STS authentication for the AWS MCP Server +description: + Learn how to centralize AWS credential management by using ToolHive to + exchange OIDC tokens for temporary AWS credentials via STS. +--- + +This tutorial shows you how to use ToolHive as an authentication proxy for the +[AWS MCP Server](https://docs.aws.amazon.com/aws-mcp/). Developers sign in +through their company identity provider, and ToolHive exchanges their OIDC token +for temporary AWS credentials via +[`AssumeRoleWithWebIdentity`](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html). + +:::info[Prerequisites] + +Before starting this tutorial, ensure you have: + +- A Kubernetes cluster with the ToolHive Operator installed (see the + [Kubernetes quickstart guide](../guides-k8s/quickstart.mdx)) +- `kubectl` configured to access your cluster +- The + [AWS CLI](https://docs.aws.amazon.com/cli/latest/userguide/getting-started-install.html) + installed and configured with an AWS account that has permissions to create + IAM roles, policies, and OIDC identity providers +- An OIDC identity provider (such as Okta, Auth0, Microsoft Entra ID, or + Keycloak) that your users authenticate with +- Basic familiarity with AWS IAM concepts and OIDC + +::: + +## Overview + +The following diagram shows how ToolHive processes each request: + +```mermaid +flowchart LR + subgraph client["Client"] + user["User"] + end + + subgraph gateway["ToolHive"] + authn["OIDC Auth"] + mapper["Role Mapper"] + sts["STS Exchange"] + signer["SigV4 Signer"] + end + + subgraph aws["AWS"] + stsapi["AWS STS"] + mcp["AWS MCP Server"] + end + + user -->|"JWT"| authn + authn --> mapper + mapper -->|"Selected Role ARN"| sts + sts <-->|"AssumeRoleWithWebIdentity"| stsapi + sts --> signer + signer -->|"SigV4 Signed Request"| mcp +``` + +1. The client sends a request with an OIDC token from your identity provider. +2. ToolHive validates the token against your OIDC provider's JWKS endpoint. +3. The **role mapper** inspects JWT claims (such as `groups`) and selects an IAM + role based on your configured mappings. +4. ToolHive calls AWS STS `AssumeRoleWithWebIdentity` to exchange the OIDC token + for temporary AWS credentials. +5. The **SigV4 signer** signs the outgoing request with the temporary + credentials. +6. The signed request is forwarded to the AWS MCP Server. + +The AWS MCP Server acts as the access point that allows AI assistants to connect +to different AWS services. Compared to configuring AWS credentials directly, +ToolHive adds: + +- **Company IdP integration:** Developers authenticate using company SSO. + ToolHive acquires short-lived, properly scoped credentials on their behalf. No + AWS CLI configuration is required, and AWS credentials are never stored on + developers' machines. +- **Fine-grained authorization:** Control which MCP tools a user can invoke. + Cedar policies are evaluated on every request, using claims from the incoming + token to make access decisions. +- **Observability, metrics, and auditing:** ToolHive provides OpenTelemetry + tracing, Prometheus metrics, and audit logs that correlate user identity + across the request flow. + +## Step 1: Register your identity provider with AWS IAM + +Create an OIDC identity provider in AWS IAM so that AWS STS trusts tokens from +your identity provider. + +```bash +aws iam create-open-id-connect-provider \ + --url https:// \ + --client-id-list +``` + +Replace the placeholders: + +- `` - your identity provider's issuer identifier without the + `https://` scheme. Include any path components (for example, + `dev-123456.okta.com/oauth2/default`) +- `` - the audience claim in your OIDC tokens that + identifies this proxy (for example, `toolhive-aws-proxy`) + +:::info[What's happening?] + +This step establishes a trust relationship between AWS and your identity +provider. When ToolHive later calls `AssumeRoleWithWebIdentity`, AWS STS +verifies the OIDC token signature against keys published by your identity +provider. Without this trust relationship, AWS rejects the token exchange. + +::: + +## Step 2: Create IAM roles and policies + +Create IAM roles that ToolHive assumes on behalf of your users. Each role +defines what AWS permissions a user gets when their JWT claims match a role +mapping. + +### Understanding the AWS MCP Server permission model + +AWS MCP Server authorization works in two layers: + +1. **MCP layer** (`aws-mcp:*` actions) - controls which categories of MCP tools + the user can invoke +2. **AWS service layer** (e.g., `s3:*`, `ec2:*`) - controls what the + `aws___call_aws` tool can actually do when it makes AWS API calls + +The `aws-mcp` namespace defines three actions: + +- `InvokeMcp` - required to connect and discover available tools +- `CallReadOnlyTool` - search documentation, list regions, get CLI suggestions + (most tools) +- `CallReadWriteTool` - execute real AWS API calls via the `aws___call_aws` tool + (requires additional service permissions) + +### Default role + +Create a role with minimal permissions. This is the fallback when no specific +role mapping matches. It needs two policy documents. + +The trust policy allows AWS STS to accept tokens from your OIDC provider. The +`Federated` principal identifies your registered provider, and the `aud` +condition rejects tokens meant for other services: + +```json title="default-mcp-trust-policy.json" +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Principal": { + "Federated": "arn:aws:iam:::oidc-provider/" + }, + "Action": "sts:AssumeRoleWithWebIdentity", + "Condition": { + "StringEquals": { + ":aud": "" + } + } + } + ] +} +``` + +The permission policy grants read-only MCP access. Users can search AWS +documentation and get suggestions, but cannot execute AWS API calls: + +```json title="default-mcp-permissions.json" +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": ["aws-mcp:InvokeMcp", "aws-mcp:CallReadOnlyTool"], + "Resource": "*" + } + ] +} +``` + +Create the role with the trust policy and attach the permission policy: + +```bash +aws iam create-role \ + --role-name DefaultMCPRole \ + --assume-role-policy-document file://default-mcp-trust-policy.json + +aws iam put-role-policy \ + --role-name DefaultMCPRole \ + --policy-name DefaultMCPPolicy \ + --policy-document file://default-mcp-permissions.json +``` + +:::warning[Production: use permission boundaries] + +For production deployments, attach +[permission boundaries](https://docs.aws.amazon.com/IAM/latest/UserGuide/access_policies_boundaries.html) +to your IAM roles to limit the maximum permissions a role can grant. A good +boundary policy restricts allowed AWS regions, denies IAM and Organizations API +access, and denies `sts:AssumeRole` to prevent role chaining. This limits the +blast radius even if a role's identity policy is overly permissive. + +::: + +### Optional: additional roles for specific teams + +You can create additional roles with different permissions and map them to +specific groups using ToolHive's role mappings. This example creates a role that +grants `CallReadWriteTool` (so the `aws___call_aws` tool can execute API calls) +and scopes the underlying AWS permissions to S3 read-only access: + +```json title="s3-readonly-permissions.json" +{ + "Version": "2012-10-17", + "Statement": [ + { + "Effect": "Allow", + "Action": [ + "aws-mcp:InvokeMcp", + "aws-mcp:CallReadOnlyTool", + "aws-mcp:CallReadWriteTool" + ], + "Resource": "*" + }, + { + "Effect": "Allow", + "Action": ["s3:GetObject", "s3:ListBucket"], + "Resource": "*" + } + ] +} +``` + +The first statement unlocks the `aws___call_aws` tool. The second statement +limits what that tool can actually do - in this case, only S3 read operations. +Without the S3 permissions, API calls to other services would be denied by IAM. + +```bash +aws iam create-role \ + --role-name S3ReadOnlyMCPRole \ + --assume-role-policy-document file://default-mcp-trust-policy.json + +aws iam put-role-policy \ + --role-name S3ReadOnlyMCPRole \ + --policy-name S3ReadOnlyMCPPolicy \ + --policy-document file://s3-readonly-permissions.json +``` + +## Step 3: Create the MCPExternalAuthConfig + +Create an `MCPExternalAuthConfig` resource that defines how ToolHive exchanges +OIDC tokens for AWS credentials. + +```yaml {4,7} title="aws-sts-auth-config.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPExternalAuthConfig +metadata: + name: aws-mcp-sts-auth + namespace: toolhive-system +spec: + type: awsSts + awsSts: + region: + + # Default role when no role mapping matches + fallbackRoleArn: >- + arn:aws:iam:::role/DefaultMCPRole + + # Map JWT claims to IAM roles (lower priority = evaluated first) + roleMappings: + - claim: s3-readers + roleArn: >- + arn:aws:iam:::role/S3ReadOnlyMCPRole + priority: 10 +``` + +Replace the placeholders: + +- `` - the AWS region (for example, `us-east-1`) +- `` - your 12-digit AWS account ID + +:::info[How role selection works] + +When a request arrives, ToolHive evaluates your role mappings in priority order +(lower number = higher priority). The first matching rule determines which IAM +role to assume. If no mapping matches, the fallback role is used. + +For example, if a user belongs to both `s3-readers` and `developers` groups, and +`s3-readers` has a lower priority number, ToolHive selects the S3 read-only +role. + +::: + +Apply the configuration: + +```bash +kubectl apply -f aws-sts-auth-config.yaml +``` + +:::info[What's happening?] + +ToolHive checks the `groups` claim in the JWT by default (controlled by the +`roleClaim` field, which defaults to `"groups"` when omitted). Each mapping's +`claim` field is the **value** to match against that claim. In this example, if +the user's JWT contains `"s3-readers"` in their `groups` array, ToolHive assumes +the S3 read-only role. + +If your identity provider uses a different claim name (e.g., `roles` or +`memberOf`), add the `roleClaim` field: + +```yaml +awsSts: + roleClaim: roles # look at the "roles" claim instead of "groups" +``` + +For more complex matching logic, use CEL expressions in the `matcher` field +instead of `claim`: + +```yaml +roleMappings: + - matcher: '"admins" in claims["groups"] && claims["org"] == "engineering"' + roleArn: arn:aws:iam::123456789012:role/AdminMCPRole + priority: 1 +``` + +::: + +## Step 4: Deploy the MCPRemoteProxy + +Create an `MCPRemoteProxy` resource that points to the AWS MCP Server endpoint +and references the authentication configuration from the previous step. + +```yaml {7,10-11,14-19} title="aws-mcp-remote-proxy.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPRemoteProxy +metadata: + name: aws-mcp-proxy + namespace: toolhive-system +spec: + remoteURL: https://aws-mcp.us-east-1.api.aws/mcp + + # Reference the AWS STS auth config from Step 3 + externalAuthConfigRef: + name: aws-mcp-sts-auth + + # OIDC configuration for validating incoming client tokens + oidcConfig: + type: inline + inline: + issuer: https:// + audience: + clientId: + + proxyPort: 8080 + transport: streamable-http + + audit: + enabled: true + + resources: + limits: + cpu: '500m' + memory: 512Mi + requests: + cpu: 100m + memory: 128Mi +``` + +Replace the placeholders with your OIDC provider's configuration. + +Apply the proxy: + +```bash +kubectl apply -f aws-mcp-remote-proxy.yaml +``` + +:::info[What's happening?] + +When you apply this resource, the ToolHive Operator: + +1. Creates a Deployment running the ToolHive proxy +2. Creates a Service to expose the proxy within the cluster +3. Configures the proxy to validate incoming OIDC tokens, exchange them for AWS + credentials via STS, and forward SigV4-signed requests to the AWS MCP Server + +::: + +## Step 5: Expose the proxy + +To make the proxy accessible to clients outside the cluster, create Gateway and +HTTPRoute resources. This example uses Kubernetes +[Gateway API](https://gateway-api.sigs.k8s.io/); if your cluster uses a +traditional Ingress controller, see +[Connect clients to MCP servers](../guides-k8s/connect-clients.mdx) for +alternatives. + +```yaml title="aws-mcp-gateway.yaml" +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: aws-mcp-gateway + namespace: toolhive-system +spec: + gatewayClassName: + listeners: + - name: https + protocol: HTTPS + port: 443 + hostname: + allowedRoutes: + namespaces: + from: All +--- +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: aws-mcp-route + namespace: toolhive-system +spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: aws-mcp-gateway + namespace: toolhive-system + hostnames: + - + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: mcp-aws-mcp-proxy-remote-proxy + port: 8080 +``` + +Replace `` and `` with your gateway +configuration. + +```bash +kubectl apply -f aws-mcp-gateway.yaml +``` + +For more on exposing MCP servers, see +[Connect clients to MCP servers](../guides-k8s/connect-clients.mdx). For a +worked example using ngrok for development, see +[Configure secure ingress for MCP servers on Kubernetes](./ingress-ngrok.mdx). + +## Step 6: Verify the integration + +Check that all resources are running: + +```bash +# Verify the MCPExternalAuthConfig +kubectl get mcpexternalauthconfig -n toolhive-system + +# Verify the MCPRemoteProxy +kubectl get mcpremoteproxy -n toolhive-system + +# Check the proxy pods are running +kubectl get pods -n toolhive-system -l app.kubernetes.io/instance=aws-mcp-proxy +``` + +Test that unauthenticated requests are rejected: + +```bash +# Port-forward for local testing +kubectl port-forward -n toolhive-system \ + svc/mcp-aws-mcp-proxy-remote-proxy 8080:8080 & + +# This should return 401 Unauthorized +curl -s -o /dev/null -w "%{http_code}" \ + -X POST http://localhost:8080/mcp \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' +``` + +Now test with a valid OIDC token. This example uses +[oauth2c](https://github.com/cloudentity/oauth2c) and +[jq](https://jqlang.github.io/jq/) to obtain and extract a token, but any method +that produces a valid access token from your identity provider will work: + +```bash +TOKEN=$(oauth2c https:// \ + --client-id \ + --client-secret $OIDC_CLIENT_SECRET \ + --scopes openid \ + --grant-type authorization_code \ + --auth-method client_secret_basic \ + --response-mode form_post \ + --response-types code \ + --pkce | jq -r '.access_token') + +# This should return a list of tools +curl -X POST http://localhost:8080/mcp \ + -H "Authorization: Bearer $TOKEN" \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","method":"tools/list","id":1}' +``` + +Check the proxy logs to confirm role selection is working: + +```bash +kubectl logs -n toolhive-system \ + -l app.kubernetes.io/instance=aws-mcp-proxy --tail=50 +``` + +Look for log entries showing role selection and STS exchange results. + +## Observability and audit + +ToolHive sets the STS session name to the user's `sub` claim from their JWT. +This means you can correlate ToolHive proxy logs with AWS CloudTrail entries for +the same user - look for CloudTrail events with +`eventName: AssumeRoleWithWebIdentity` and check +`requestParameters.roleSessionName` to identify who triggered each action. + +On the ToolHive side, the proxy logs role selection and STS exchange events +(user identity, matched claim, selected role, success or failure). You can also +enable Prometheus metrics and OpenTelemetry tracing on the MCPRemoteProxy - see +[Telemetry and metrics](../guides-k8s/telemetry-and-metrics.mdx) and the +[OpenTelemetry tutorial](./opentelemetry.mdx) for setup instructions. + +## Clean up + +Remove the Kubernetes resources: + +```bash +kubectl delete mcpremoteproxy aws-mcp-proxy -n toolhive-system +kubectl delete mcpexternalauthconfig aws-mcp-sts-auth -n toolhive-system +kubectl delete gateway aws-mcp-gateway -n toolhive-system +kubectl delete httproute aws-mcp-route -n toolhive-system +``` + +Remove the AWS IAM resources: + +```bash +aws iam delete-role-policy \ + --role-name DefaultMCPRole --policy-name DefaultMCPPolicy +aws iam delete-role --role-name DefaultMCPRole + +# If you created the optional S3 read-only role +aws iam delete-role-policy \ + --role-name S3ReadOnlyMCPRole --policy-name S3ReadOnlyMCPPolicy +aws iam delete-role --role-name S3ReadOnlyMCPRole + +aws iam delete-open-id-connect-provider \ + --open-id-connect-provider-arn \ + arn:aws:iam:::oidc-provider/ +``` + +## What's next? + +- Learn about the concepts behind + [backend authentication](../concepts/backend-auth.mdx) and + [token exchange](../guides-cli/token-exchange.mdx). +- Explore the + [authentication and authorization framework](../concepts/auth-framework.mdx) + for securing client-to-MCP-server connections. +- Read the AWS documentation on + [AssumeRoleWithWebIdentity](https://docs.aws.amazon.com/STS/latest/APIReference/API_AssumeRoleWithWebIdentity.html) + and + [IAM OIDC identity providers](https://docs.aws.amazon.com/IAM/latest/UserGuide/id_roles_providers_create_oidc.html). + +## Troubleshooting + +
+STS access denied: "Not authorized to perform +sts:AssumeRoleWithWebIdentity" + +This error means the IAM role's trust policy doesn't allow your OIDC provider to +assume it. Verify: + +- The OIDC provider ARN in the trust policy's `Federated` field matches the + provider you registered in Step 1. +- The `aud` condition matches the audience in your OIDC tokens. +- The OIDC provider's issuer URL in AWS IAM exactly matches the `iss` claim in + your tokens (including any path like `/oauth2/default`). + +Check the trust policy: + +```bash +aws iam get-role --role-name DefaultMCPRole \ + --query 'Role.AssumeRolePolicyDocument' +``` + +
+ +
+Service access denied: "User is not authorized to perform +aws-mcp:InvokeMcp" + +This error means the assumed role doesn't have the required permissions. Verify +the permission policy attached to the role includes the necessary actions: + +```bash +aws iam get-role-policy \ + --role-name DefaultMCPRole \ + --policy-name DefaultMCPPolicy +``` + +Ensure the policy includes `aws-mcp:InvokeMcp` and any other actions your MCP +tools require. + +
+ +
+Debugging token claims and role selection + +Obtain a token and inspect the output to verify your claims: + +```bash +oauth2c https:// \ + --client-id \ + --client-secret $OIDC_CLIENT_SECRET \ + --scopes openid \ + --grant-type authorization_code \ + --auth-method client_secret_basic \ + --response-mode form_post \ + --response-types code \ + --pkce +``` + +Check that the claim specified by `roleClaim` (default: `groups`) contains the +expected values. For example, if your role mapping uses `claim: s3-readers`, the +decoded token should include: + +```json +{ + "groups": ["s3-readers", "developers"] +} +``` + +If role mappings aren't matching as expected, check the proxy logs for role +selection details: + +```bash +kubectl logs -n toolhive-system \ + -l app.kubernetes.io/instance=aws-mcp-proxy | grep -i "role" +``` + +
+ +
+Proxy returns 401 Unauthorized + +If clients receive 401 errors, the OIDC token validation is failing. Verify: + +- The `issuer` in `oidcConfig` matches the `iss` claim in your token. +- The `audience` matches the `aud` claim. +- The token hasn't expired (check the `exp` claim). +- The proxy can reach your OIDC provider's JWKS endpoint from within the + cluster. + +Check proxy logs for authentication errors: + +```bash +kubectl logs -n toolhive-system \ + -l app.kubernetes.io/instance=aws-mcp-proxy | grep -i "auth" +``` + +
diff --git a/versioned_docs/version-1.1/toolhive/integrations/ingress-ngrok.mdx b/versioned_docs/version-1.1/toolhive/integrations/ingress-ngrok.mdx new file mode 100644 index 00000000..02aca2d1 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/integrations/ingress-ngrok.mdx @@ -0,0 +1,384 @@ +--- +title: Configure secure ingress for MCP servers on Kubernetes +description: + A guide to setting up secure ingress for MCP servers running on a Kubernetes + cluster using the ToolHive Operator. +--- + +In this tutorial, you'll configure secure ingress for MCP servers running on a +Kubernetes cluster using the ToolHive Operator. By the end, you'll have a +working MCP server accessible via a secure HTTPS endpoint that your team can +use. + +You'll use the [ngrok Kubernetes Operator](https://ngrok.com/docs/k8s) to create +secure tunnels to your MCP servers. This setup ensures that your MCP servers are +accessible over HTTPS, providing a secure way to interact with them. While this +tutorial uses ngrok for simplicity, you can apply similar principles with other +Kubernetes gateway solutions that work with the Kubernetes Gateway API, such as +[Traefik](https://doc.traefik.io/traefik/), +[Istio](https://istio.io/latest/docs/tasks/traffic-management/ingress/gateway-api/), +and +[many others](https://gateway-api.sigs.k8s.io/implementations/#implementations_1). + +:::tip + +This tutorial demonstrates ngrok specifically. For general guidance on exposing +MCP servers with Ingress, Gateway API, or cloud provider implementations, see +[Connect clients to MCP servers](../guides-k8s/connect-clients.mdx). + +::: + +## What you'll learn + +This tutorial demonstrates how to make MCP servers available centrally for teams +and enterprises. By deploying MCP servers on Kubernetes with secure ingress, you +create a shared pool of capabilities that any team member can access. This +approach is valuable for organizations that want to provide standardized tools +and resources to developers while maintaining control over security, access +policies, and resource usage. + +Once your MCP servers are accessible via HTTPS, users can quickly connect their +AI clients using the ToolHive CLI (`thv run`) or UI without needing to install +or configure individual MCP servers locally. This centralized model simplifies +deployment, improves consistency, and makes it easier to manage updates and +security patches. + +In this tutorial, you'll learn: + +- How to install and configure the ngrok Kubernetes Operator. +- How to create secure tunnels for MCP servers. +- How to access MCP servers via HTTPS. + +## Prerequisites + +Before you begin, ensure you have the following: + +- A Kubernetes cluster with the ToolHive Operator installed. See the + [Kubernetes quickstart guide](../guides-k8s/quickstart.mdx) for instructions. +- `kubectl` command-line tool configured to interact with your cluster. +- An [ngrok account](https://ngrok.com/) to obtain an authentication token. A + free account is sufficient for this tutorial. +- The [ToolHive CLI](../guides-cli/install.mdx) to interact with the remote MCP + server and connect it to your AI clients. + +## Step 1: Install the ngrok Kubernetes Operator + +These steps are a simplified version of the instructions found in the +[ngrok documentation](https://ngrok.com/docs/getting-started/kubernetes/gateway-api). + +First, you'll need to install the ngrok Kubernetes Operator in your cluster +using Helm: + +```bash +helm repo add ngrok https://charts.ngrok.com +helm repo update +``` + +Obtain your authentication token and create an API key from the ngrok dashboard, +then set them as environment variables: + +```bash +export NGROK_AUTHTOKEN="your_ngrok_auth_token" +export NGROK_API_KEY="your_ngrok_api_key" +``` + +Install the Kubernetes Gateway API CRDs and a GatewayClass resource: + +```bash +kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/standard-install.yaml + +kubectl apply -f -<-proxy`. For the MKP example, the +service is `mcp-mkp-proxy` in the `toolhive-system` namespace on port `8080`: + +```bash +kubectl get service mcp-mkp-proxy -n toolhive-system +``` + +The output should look similar to this: + +```plaintext +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +mcp-mkp-proxy ClusterIP 10.96.106.88 8080/TCP 2m19s +``` + +Note the service name and port number for the next step. + +## Step 3: Create Gateway and HTTPRoute resources + +Now, create a Gateway and HTTPRoute resource to expose the MCP server securely +via ngrok. + +For this step, you'll need to obtain your dev domain or custom domain from the +[ngrok dashboard](https://dashboard.ngrok.com/domains). If you have a free +account, it will be in the format `.ngrok-free.app`. Replace +`` with your actual domain in the YAML below. + +Create a file named `ngrok-mcp-gateway.yaml` with the following content: + +```yaml {12} title="ngrok-mcp-gateway.yaml" +apiVersion: gateway.networking.k8s.io/v1 +kind: Gateway +metadata: + name: ngrok-gateway + namespace: toolhive-system +spec: + gatewayClassName: ngrok + listeners: + - name: https + protocol: HTTPS + port: 443 + hostname: + allowedRoutes: + namespaces: + from: All +``` + +Then create a file named `ngrok-mcp-httproute.yaml` with the following content: + +```yaml {13,20-22} title="ngrok-mcp-httproute.yaml" +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: mkp-mcp-route + namespace: toolhive-system +spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: ngrok-gateway + namespace: toolhive-system + hostnames: + - + rules: + - matches: + - path: + type: PathPrefix + value: / + backendRefs: + - name: mcp-mkp-proxy # Replace with your service name from Step 2 + port: 8080 # Replace with your port number from Step 2 +``` + +Apply the configurations to your cluster: + +```bash +kubectl apply -f ngrok-mcp-gateway.yaml +kubectl apply -f ngrok-mcp-httproute.yaml +``` + +## Step 4: Access the MCP server + +After a few moments, the ngrok Operator will create a secure tunnel to your MCP +server. You can access it using the domain you specified in the Gateway +resource. + +Use the ToolHive CLI to verify connectivity to the MCP server: + +```bash +thv mcp list tools --server https:///mcp +``` + +The `/mcp` path is the default endpoint for the MCP Streamable HTTP transport. +The output should display a list of tools managed by the MCP server, confirming +that you have successfully set up secure ingress using ngrok. For the "MKP" MCP +server, you should see output similar to this: + +```plaintext +TOOLS: +NAME DESCRIPTION +get_resource Get a Kubernetes resource or its subresource +list_resources List Kubernetes resources +``` + +Use the ToolHive CLI or UI to connect your AI clients to the MCP server: + +```bash +thv run --name mkp https:///mcp +``` + +The MKP MCP server is now available to AI clients configured with +`thv client setup` via a secure HTTPS endpoint. + +## Optional: Tunnel multiple MCP servers with URL rewrites + +The previous steps exposed a single MCP server at the root path (`/`). If you +have multiple MCP servers and want to expose them via the same ngrok Gateway, +you can use path-based routing with URL rewrites in the HTTPRoute resource. This +allows you to route requests to different MCP servers based on path prefixes +like `/mkp` and `/fetch`. + +:::note + +This option consumes $1 of the $5 credit included with the free ngrok account to +enable the traffic policies feature or requires a paid ngrok plan. + +::: + +Run a second MCP server, for example: + +```bash +kubectl apply -f https://raw.githubusercontent.com/stacklok/toolhive/refs/heads/main/examples/operator/mcp-servers/mcpserver_fetch.yaml +``` + +Then, update the `ngrok-mcp-httproute.yaml` file. Update the `rules` section of +the existing HTTPRoute resource to give the MKP MCP server a path prefix of +`/mkp` and add a `URLRewrite` filter. + +```yaml {18,22-27} showLineNumbers title="ngrok-mcp-httproute.yaml" +apiVersion: gateway.networking.k8s.io/v1 +kind: HTTPRoute +metadata: + name: mcp-servers-route + namespace: toolhive-system +spec: + parentRefs: + - group: gateway.networking.k8s.io + kind: Gateway + name: ngrok-gateway + namespace: toolhive-system + hostnames: + - + rules: + - matches: + - path: + type: PathPrefix + value: /mkp + backendRefs: + - name: mcp-mkp-proxy + port: 8080 + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplacePrefixMatch + replacePrefixMatch: '' # Normally this would be '/' but ngrok requires empty string +``` + +Add another rule for the Fetch MCP server with a path prefix of `/fetch`: + +{/* prettier-ignore */} +```yaml showLineNumbers=28 title="ngrok-mcp-httproute.yaml" + - matches: + - path: + type: PathPrefix + value: /fetch + backendRefs: + - name: mcp-fetch-proxy + port: 8080 + filters: + - type: URLRewrite + urlRewrite: + path: + type: ReplacePrefixMatch + replacePrefixMatch: '' +``` + +Apply the updated configuration to your cluster: + +```bash +kubectl apply -f ngrok-mcp-httproute.yaml +``` + +You can now access both MCP servers using the same ngrok domain with different +path prefixes: + +```bash +thv mcp list tools --server https:///mkp/mcp +thv mcp list tools --server https:///fetch/mcp +``` + +Use the ToolHive CLI or UI to connect your AI clients to either MCP server: + +```bash +thv run --name mkp https:///mkp/mcp +thv run --name fetch https:///fetch/mcp +``` + +## Clean up + +To remove the ngrok resources from your cluster and ngrok account, run the +following: + +```bash +# Delete the HTTPRoute and Gateway resources +kubectl delete -f ngrok-mcp-httproute.yaml +kubectl delete -f ngrok-mcp-gateway.yaml + +# Delete the ngrok CRDs +kubectl delete $(kubectl get crd -o name | grep "ngrok") + +# Uninstall the ngrok Operator +helm uninstall ngrok-operator -n ngrok-operator +kubectl delete namespace ngrok-operator +``` + +## What's next? + +Now that you have secure ingress configured for your MCP servers, consider these +next steps: + +- Explore [authentication and authorization](../concepts/auth-framework.mdx) to + control access to your MCP servers. +- Learn about [observability](../concepts/observability.mdx) to monitor your MCP + server usage and performance. +- Try other gateway solutions like Traefik or Istio if they're already part of + your infrastructure. + +## Addendum: Combining with MCP server authentication + +When exposing MCP servers via ngrok or any other ingress solution, consider the +security implications. While ngrok provides secure HTTPS tunnels, you should +also implement authentication and authorization to control access. The ToolHive +Operator supports +[OAuth-based authentication methods](../guides-k8s/auth-k8s.mdx) that are out of +scope for this tutorial but essential for production deployments. + +When OAuth is enabled on an MCP server, add additional rules to the HTTPRoute +resource to expose the OAuth metadata endpoint for proper authentication flow +through the gateway. + +Here's an example rule to add to the `mcp-servers-route` HTTPRoute in your +`ngrok-mcp-httproute.yaml` file. Add this rule alongside the existing `/mkp` +path rule: + +{/* prettier-ignore */} +```yaml title="ngrok-mcp-httproute.yaml" + - matches: + - path: + type: Exact + value: /.well-known/oauth-protected-resource/mkp/mcp + backendRefs: + - name: mcp-mkp-proxy + port: 8080 +``` diff --git a/versioned_docs/version-1.1/toolhive/integrations/okta.mdx b/versioned_docs/version-1.1/toolhive/integrations/okta.mdx new file mode 100644 index 00000000..ab5680de --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/integrations/okta.mdx @@ -0,0 +1,492 @@ +--- +title: Role-based authorization with Okta +description: + Deploy a GitHub MCP server on Kubernetes, add Okta OIDC authentication, and + enforce role-based access control (RBAC) with Cedar policies. +--- + +Without authorization, every authenticated user has access to every tool an MCP +server exposes. By the end of this tutorial, you'll have a GitHub MCP server on +Kubernetes secured with Okta OpenID Connect (OIDC) authentication and Cedar +role-based access control (RBAC) policies — writers get full access while +readers see only read-only tools. + +:::tip[Using a different identity provider?] + +This tutorial uses Okta, but the pattern applies to any OIDC provider. Only Step +1 is Okta-specific — it covers creating an OIDC application, setting up groups, +and adding a groups claim to the token. If you use a different provider +(Keycloak, Entra ID, Auth0, etc.), complete the equivalent setup in your +provider, then pick up from [Step 2](#step-2-deploy-the-github-mcp-server) +onward. The Kubernetes manifests, Cedar policies, and `oidcConfig` fields all +consume standard OIDC values regardless of which provider issued them. + +::: + +## Prerequisites + +:::info[Prerequisites] + +Before starting this tutorial, make sure you have: + +- A Kubernetes cluster with the ToolHive Operator installed. See the + [Kubernetes quickstart guide](../guides-k8s/quickstart.mdx) for setup + instructions. +- `kubectl` configured to access your cluster +- A + [GitHub personal access token](https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens) + (PAT) with `repo` scope +- An Okta developer account (free at + [developer.okta.com](https://developer.okta.com/)) +- Basic familiarity with OAuth, OIDC, and JSON Web Token (JWT) concepts. For + background, see + [Authentication and authorization framework](../concepts/auth-framework.mdx). + +::: + +## Step 1: Configure Okta + +Set up your Okta environment with an OIDC application, user groups, and a groups +claim so that ToolHive can authenticate users and read their group memberships +from JWTs. + +### Create an OIDC application + +1. Sign in to the Okta admin console. +2. Go to **Applications** > **Applications** > **Create App Integration**. +3. Select **OIDC - OpenID Connect** and **Web Application**, then click + **Next**. +4. Set the sign-in redirect URI to `http://localhost:8080/callback` (for local + testing). +5. Under **Assignments**, assign the app to the groups you create in the next + section. +6. Click **Save**. + +### Create groups and users + +1. Go to **Directory** > **Groups**. +2. Create two groups: `mcp-read` and `mcp-write`. +3. Create (or assign) two test users. Add Alice to `mcp-read` only. Add Bob to + both `mcp-read` and `mcp-write`. + +### Add a groups claim to your identity provider + +1. Go to **Security** > **API** > **Authorization Servers**. +2. Select the `default` authorization server (or your custom one). +3. Go to **Claims** > **Add Claim**. +4. Set the following values: + - **Name**: `groups` + - **Include in**: **ID Token** and **Access Token** + - **Value type**: **Groups** + - **Filter**: **Matches regex** `.*` +5. Click **Create**. +6. Note the **Issuer URI** from the authorization server settings (for example, + `https://YOUR_OKTA_DOMAIN/oauth2/default`). + +### Collect your configuration values + +After setup, you need three values from Okta. Use the table below to locate each +one: + +| Value | Where to find it | Example | +| ---------- | ------------------------------------------------------------------------------------------- | --------------------------------------------------- | +| Issuer URL | **Security** > **API** > **Authorization Servers** > Issuer URI | `https://dev-12345.okta.com/oauth2/default` | +| Audience | `api://default` for the default authorization server, or the custom audience you configured | `api://default` | +| JWKS URL | Issuer URL + `/v1/keys` | `https://dev-12345.okta.com/oauth2/default/v1/keys` | + +## Step 2: Deploy the GitHub MCP server + +### Create a Secret for your GitHub PAT + +Store your GitHub personal access token as a Kubernetes Secret. + +:::tip + +Your PAT needs the `repo` scope for write tools (like `create_pull_request` and +`push_files`) to appear. Without it, the GitHub MCP server only exposes +read-only tools regardless of your Cedar policies. + +::: + +```yaml title="github-pat-secret.yaml" +apiVersion: v1 +kind: Secret +metadata: + name: github-pat + namespace: toolhive-system +type: Opaque +stringData: + token: 'YOUR_GITHUB_PAT' +``` + +```bash +kubectl apply -f github-pat-secret.yaml +``` + +### Deploy the MCPServer without authentication + +Create the MCPServer resource without authentication to verify the basic +deployment works: + +```yaml title="github-mcpserver.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: github + namespace: toolhive-system +spec: + image: ghcr.io/github/github-mcp-server:latest + transport: stdio + secrets: + - name: github-pat + key: token + targetEnvName: GITHUB_PERSONAL_ACCESS_TOKEN + resources: + limits: + cpu: '200m' + memory: '256Mi' + requests: + cpu: '100m' + memory: '128Mi' +``` + +```bash +kubectl apply -f github-mcpserver.yaml +``` + +### Verify the server is running + +Check the status of the MCPServer resource: + +```bash +kubectl get mcpserver -n toolhive-system github +``` + +You should see output similar to: + +```text +NAME STATUS URL AGE +github Running http://mcp-github-proxy.toolhive-system.svc.cluster.local:8080 30s +``` + +Wait until the status shows `Running` before continuing to the next step. If the +server remains in a pending state, check the operator logs for errors: + +```bash +kubectl logs -n toolhive-system deployment/toolhive-operator +``` + +## Step 3: Add Okta OIDC authentication + +Update the MCPServer to include an `oidcConfig` section. Replace the placeholder +values with the configuration values you collected in +[Step 1](#collect-your-configuration-values): + +```yaml title="github-mcpserver-oidc.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: github + namespace: toolhive-system +spec: + image: ghcr.io/github/github-mcp-server:latest + transport: stdio + secrets: + - name: github-pat + key: token + targetEnvName: GITHUB_PERSONAL_ACCESS_TOKEN + # highlight-start + oidcConfig: + type: inline + inline: + issuer: 'YOUR_ISSUER_URL' + audience: 'YOUR_AUDIENCE' + jwksUrl: 'YOUR_JWKS_URL' + # highlight-end + # ... resources same as before +``` + +```bash +kubectl apply -f github-mcpserver-oidc.yaml +``` + +After applying this change, the MCP server requires a valid JWT on every +request. Unauthenticated requests now return `401 Unauthorized`. + +:::tip + +To test authenticated requests, you can use a tool like +[oauth2c](https://github.com/cloudentity/oauth2c) to obtain tokens from Okta, or +use your Okta admin console to generate a test token from **Security** > +**API** > **Authorization Servers** > **Token Preview**. + +::: + +## Step 4: Add Cedar policies for role-based access + +Now that authentication is in place, add authorization policies. In this step, +you define Cedar policies that give writers full access while restricting +readers to a set of read-only tools. + +### Define the roles + +The following table summarizes the two roles and the tools each role can access: + +| Role | Group | Allowed tools | +| ------ | ----------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| Writer | `mcp-write` | All tools (read and write) | +| Reader | `mcp-read` | Read-only tools: `get_file_contents`, `list_commits`, `list_branches`, `list_issues`, `list_pull_requests`, `search_issues`, `get_me` | + +### Create the authorization ConfigMap + +:::note + +ToolHive exposes JWT claims to Cedar policies with a `claim_` prefix, so the +`groups` claim you configured in Okta becomes `principal.claim_groups` in policy +expressions. For more details, see +[Working with JWT claims](../concepts/cedar-policies.mdx#working-with-jwt-claims). + +::: + +Create a ConfigMap containing the Cedar policies: + +```yaml title="authz-config.yaml" +apiVersion: v1 +kind: ConfigMap +metadata: + name: github-authz + namespace: toolhive-system +data: + authz-config.json: | + { + "version": "1.0", + "type": "cedarv1", + "cedar": { + "policies": [ + "permit(principal, action, resource) when { principal.claim_groups.contains(\"mcp-write\") };", + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"get_file_contents\") when { principal.claim_groups.contains(\"mcp-read\") };", + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"list_commits\") when { principal.claim_groups.contains(\"mcp-read\") };", + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"list_branches\") when { principal.claim_groups.contains(\"mcp-read\") };", + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"list_issues\") when { principal.claim_groups.contains(\"mcp-read\") };", + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"list_pull_requests\") when { principal.claim_groups.contains(\"mcp-read\") };", + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"search_issues\") when { principal.claim_groups.contains(\"mcp-read\") };", + "permit(principal, action == Action::\"call_tool\", resource == Tool::\"get_me\") when { principal.claim_groups.contains(\"mcp-read\") };" + ], + "entities_json": "[]" + } + } +``` + +```bash +kubectl apply -f authz-config.yaml +``` + +### Update the MCPServer with authorization + +Add the `authzConfig` section to reference the ConfigMap you just created. The +highlighted lines show the new addition: + +```yaml title="github-mcpserver-authz.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: github + namespace: toolhive-system +spec: + image: ghcr.io/github/github-mcp-server:latest + transport: stdio + secrets: + - name: github-pat + key: token + targetEnvName: GITHUB_PERSONAL_ACCESS_TOKEN + oidcConfig: + type: inline + inline: + issuer: 'YOUR_ISSUER_URL' + audience: 'YOUR_AUDIENCE' + jwksUrl: 'YOUR_JWKS_URL' + # highlight-start + authzConfig: + type: configMap + configMap: + name: github-authz + key: authz-config.json + # highlight-end + # ... resources same as before +``` + +```bash +kubectl apply -f github-mcpserver-authz.yaml +``` + +## Step 5: Verify tool filtering + +ToolHive automatically filters the `tools/list` response based on your Cedar +policies. When a client calls `tools/list`, the proxy evaluates each tool +against the user's policies and only returns the tools the user is allowed to +call. For more details, see +[list operations and filtering](../concepts/cedar-policies.mdx#list-operations-and-filtering). + +**Writer** (Bob, in both `mcp-write` and `mcp-read`) sees all available tools: + +```text +add_issue_comment, create_branch, create_pull_request, +create_repository, get_file_contents, list_branches, +list_commits, list_issues, merge_pull_request, ... (truncated) +``` + +**Reader** (Alice, in `mcp-read` only) sees only the read-only tools permitted +by the Cedar policies: + +```text +get_file_contents, get_me, list_branches, +list_commits, list_issues, list_pull_requests, +search_issues +``` + +:::note + +Tool filtering happens automatically. You don't need separate policies for +`list_tools`. The proxy evaluates `call_tool` policies for each tool and only +returns tools the user is allowed to call. + +::: + +## Step 6: Verify denied access + +Verify that authorization is enforced by calling a write tool with a reader's +token. + +First, port-forward to the MCP server service so you can send requests from your +local machine: + +```bash +kubectl port-forward -n toolhive-system svc/mcp-github-proxy 8080:8080 +``` + +:::note + +Port-forwarding works well for testing. In production, expose your MCP servers +using an Ingress or Gateway API resource instead. See +[Connect clients to MCP servers](../guides-k8s/connect-clients.mdx) for +configuration options. + +::: + +In a separate terminal, send a request using a reader's token: + +```bash +# Using a reader's token to call a write operation +curl -X POST http://localhost:8080/mcp \ + -H "Authorization: Bearer READER_TOKEN" \ + -H "Content-Type: application/json" \ + -d '{ + "jsonrpc": "2.0", + "method": "tools/call", + "params": { + "name": "create_pull_request", + "arguments": { + "repo": "example/repo", + "title": "Test", + "head": "feature", + "base": "main" + } + }, + "id": 1 + }' +``` + +The proxy denies the request and returns a `403 Forbidden` response. Sending the +same request with a writer's token succeeds — the proxy forwards the request to +the MCP server and returns the tool's response. + +## Clean up + +Remove the resources you created in this tutorial: + +```bash +kubectl delete mcpserver -n toolhive-system github +kubectl delete configmap -n toolhive-system github-authz +kubectl delete secret -n toolhive-system github-pat +``` + +## What's next? + +- Learn more about Cedar policy syntax in + [Cedar policies](../concepts/cedar-policies.mdx) +- Explore the + [authentication and authorization framework](../concepts/auth-framework.mdx) + concepts +- Set up [token exchange](../guides-k8s/token-exchange-k8s.mdx) for downstream + service authentication +- Deploy a [Virtual MCP Server](../guides-vmcp/index.mdx) to aggregate multiple + servers behind a single endpoint + +## Troubleshooting + +
+Authentication issues + +If clients can't authenticate: + +1. Check that the JWT is valid and not expired. +2. Verify that the audience and issuer match your `oidcConfig` values. +3. Ensure the JWKS URL is accessible from within the cluster. +4. Check the operator and proxy logs for specific errors: + + ```bash + # Operator logs + kubectl logs -n toolhive-system deployment/toolhive-operator + + # Proxy logs for the GitHub MCPServer + kubectl logs -n toolhive-system \ + -l app.kubernetes.io/managed-by=toolhive,app.kubernetes.io/name=github \ + -c proxy + ``` + +
+ +
+Authorization issues + +If authenticated clients are denied access: + +1. Make sure your Cedar policies explicitly permit the specific action + (remember, default deny). +2. Check that the principal, action, and resource match what's in your policies, + including capitalization and formatting. +3. Examine any conditions in your policies to ensure they're satisfied (for + example, required JWT claims). + +
+ +
+Token missing groups claim + +Verify that the `groups` claim is configured on the **authorization server**, +not just on the application. In the Okta admin console, go to **Security** > +**API** > **Authorization Servers**, select your server, and check the +**Claims** tab. + +
+ +
+Groups not matching Cedar policies + +Group names in Cedar policies must exactly match the Okta group names, including +capitalization. For example, `mcp-write` is not the same as `MCP-Write`. Check +your Okta group names under **Directory** > **Groups** and update your Cedar +policies if needed. + +
+ +
+401 after adding oidcConfig + +Verify that the issuer URL includes the full authorization server path. For the +default Okta authorization server, the issuer URL should end with +`/oauth2/default` (for example, `https://dev-12345.okta.com/oauth2/default`). A +common mistake is to use just the Okta domain without the authorization server +path. + +
diff --git a/versioned_docs/version-1.1/toolhive/integrations/opentelemetry.mdx b/versioned_docs/version-1.1/toolhive/integrations/opentelemetry.mdx new file mode 100644 index 00000000..492581fd --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/integrations/opentelemetry.mdx @@ -0,0 +1,996 @@ +--- +title: Collect telemetry for MCP workloads +description: + Learn how to collect metrics and traces for MCP workloads using either the + ToolHive CLI or Kubernetes operator with OpenTelemetry, Jaeger, and + Prometheus. +toc_max_heading_level: 2 +--- + +import useBaseUrl from '@docusaurus/useBaseUrl'; +import ThemedImage from '@theme/ThemedImage'; + +In this tutorial, you'll set up comprehensive observability for your MCP +workloads using [OpenTelemetry](https://opentelemetry.io/) with +[Jaeger](https://www.jaegertracing.io/) for distributed tracing, +[Prometheus](https://prometheus.io/) for metrics collection, and +[Grafana](https://grafana.com/) for visualization. + +By the end, you'll have a complete, industry-standard observability solution +that captures detailed traces and metrics, giving you visibility into your MCP +server performance and usage patterns. + + + +## Choose your deployment path + +This tutorial offers two paths for MCP observability: + + + + +**ToolHive CLI + Docker observability stack** + +Use the ToolHive CLI to run MCP servers locally, with Jaeger and Prometheus +running in Docker containers. This approach is perfect for: + +- Local development and testing +- Quick setup and experimentation +- Individual developer workflows +- Learning OpenTelemetry concepts + + + + +**ToolHive Kubernetes Operator + in-cluster observability** + +Use the ToolHive Kubernetes Operator to manage MCP servers in a cluster, with +Jaeger and Prometheus deployed inside Kubernetes. This approach is ideal for: + +- Production-like environments +- Team collaboration and shared infrastructure +- Container orchestration workflows +- Scalable observability deployments + + + + +:::tip[Choose one path] + +Select your preferred deployment method using the tabs above. All subsequent +steps will show instructions for your chosen path. + +::: + +## What you'll learn + +- How to deploy Jaeger and Prometheus for your chosen environment +- How to configure OpenTelemetry collection for ToolHive MCP servers +- How to analyze traces in Jaeger and metrics in Prometheus +- How to set up queries and monitoring for MCP workloads +- Best practices for observability in your deployment environment + +## Prerequisites + +Before starting this tutorial, make sure you have: + + + + +- Completed the [ToolHive CLI quickstart](../guides-cli/quickstart.mdx) +- A supported container runtime installed and running. + [Docker](https://docs.docker.com/get-docker/) or + [Podman](https://podman-desktop.io/downloads) are recommended for this + tutorial +- [Docker Compose](https://docs.docker.com/compose/install/) or + [Podman Compose](https://podman-desktop.io/docs/compose/setting-up-compose) + available +- A [supported MCP client](../reference/client-compatibility.mdx) for testing + + + + +- Completed the [ToolHive Kubernetes quickstart](../guides-k8s/quickstart.mdx) + with a local kind cluster +- [`kubectl`](https://kubernetes.io/docs/tasks/tools/) configured to access your + cluster +- [Helm](https://helm.sh/docs/intro/install/) (v3.10 minimum) installed +- A [supported MCP client](../reference/client-compatibility.mdx) for testing +- The [ToolHive CLI](../guides-cli/quickstart.mdx) (optional, for client + configuration) +- Basic familiarity with Kubernetes concepts + + + + +## Overview + +The architecture for each deployment method: + + + + +```mermaid +graph TB + A[AI client] + THV[ToolHive CLI] + Proxy[Proxy process] + subgraph Docker[**Docker**] + MCP[MCP server
container] + OTEL[OTel Collector] + J[Jaeger] + P[Prometheus] + G[Grafana] + end + + THV -. manages .-> MCP & Proxy + Proxy -- HTTP or stdio --> MCP + Proxy -- OpenTelemetry data --> OTEL + OTEL -- traces --> J + OTEL -- metrics --> P + G -- visualization --> P + A -- HTTP --> Proxy +``` + +Your setup will include: + +- **ToolHive CLI** managing MCP servers in containers +- **Jaeger** for distributed tracing with built-in UI +- **Prometheus** for metrics collection with web UI +- **OpenTelemetry Collector** forwarding data to both backends + +
+ + +```mermaid +graph TB + A[AI client] + subgraph K8s[**K8s Cluster**] + THV[ToolHive Operator] + Proxy[Proxyrunner pod] + MCP[MCP server pod] + OTEL[OTel Collector] + J[Jaeger] + P[Prometheus] + G[Grafana] + end + + A -- HTTP
via ingress --> Proxy + THV -. manages .-> Proxy + Proxy -. manages .-> MCP + Proxy -- OpenTelemetry data --> OTEL + OTEL -- traces --> J + OTEL -- metrics --> P + G -- visualization --> P +``` + +Your setup will include: + +- **ToolHive Operator** managing MCP servers as Kubernetes pods +- **Jaeger** for distributed tracing +- **Prometheus** for metrics collection +- **Grafana** for metrics visualization +- **OpenTelemetry Collector** running as a Kubernetes service + +
+
+ +## Step 1: Deploy the observability stack + +First, set up the observability infrastructure for your chosen environment. + + + + +### Create Docker Compose configuration + +Create a Docker Compose file for the observability stack: + +```yaml title="observability-stack.yml" +services: + jaeger: + image: jaegertracing/jaeger:latest + container_name: jaeger + environment: + - COLLECTOR_OTLP_ENABLED=true + ports: + - '16686:16686' # Jaeger UI + networks: + - observability + + prometheus: + image: prom/prometheus:latest + container_name: prometheus + command: + - '--config.file=/etc/prometheus/prometheus.yml' + - '--storage.tsdb.path=/prometheus' + - '--web.console.libraries=/etc/prometheus/console_libraries' + - '--web.console.templates=/etc/prometheus/consoles' + - '--web.enable-lifecycle' + - '--enable-feature=native-histograms' + ports: + - '9090:9090' + volumes: + - ./prometheus.yml:/etc/prometheus/prometheus.yml + - prometheus-data:/prometheus + networks: + - observability + + grafana: + image: grafana/grafana:latest + container_name: grafana + environment: + - GF_SECURITY_ADMIN_USER=admin + - GF_SECURITY_ADMIN_PASSWORD=admin + - GF_USERS_ALLOW_SIGN_UP=false + ports: + - '3000:3000' + volumes: + - ./grafana-prometheus.yml:/etc/grafana/provisioning/datasources/prometheus.yml + - grafana-data:/var/lib/grafana + networks: + - observability + + otel-collector: + image: otel/opentelemetry-collector-contrib:latest + container_name: otel-collector + command: ['--config=/etc/otel-collector-config.yml'] + volumes: + - ./otel-collector-config.yml:/etc/otel-collector-config.yml + ports: + - '4318:4318' # OTLP HTTP receiver (ToolHive sends here) + - '8889:8889' # Prometheus exporter metrics + depends_on: + - jaeger + - prometheus + networks: + - observability + +volumes: + prometheus-data: + grafana-data: + +networks: + observability: + driver: bridge +``` + +### Configure the OpenTelemetry Collector + +Create the collector configuration to export to both Jaeger and Prometheus: + +```yaml title="otel-collector-config.yml" +receivers: + otlp: + protocols: + http: + endpoint: 0.0.0.0:4318 + +processors: + batch: + timeout: 10s + send_batch_size: 1024 + +exporters: + # Export traces to Jaeger + otlp/jaeger: + endpoint: jaeger:4317 + tls: + insecure: true + + # Expose metrics for Prometheus + prometheus: + endpoint: 0.0.0.0:8889 + const_labels: + service: 'toolhive-mcp-proxy' + +service: + pipelines: + traces: + receivers: [otlp] + processors: [batch] + exporters: [otlp/jaeger] + metrics: + receivers: [otlp] + processors: [batch] + exporters: [prometheus] +``` + +### Configure Prometheus and Grafana + +Create a Prometheus configuration to scrape the OpenTelemetry Collector: + +```yaml title="prometheus.yml" +global: + scrape_interval: 15s + +scrape_configs: + - job_name: 'otel-collector' + static_configs: + - targets: ['otel-collector:8889'] +``` + +Create the Prometheus data source configuration for Grafana: + +```yaml title="grafana-prometheus.yml" +apiVersion: 1 + +datasources: + - name: prometheus + type: prometheus + access: proxy + url: http://prometheus:9090 + isDefault: true + editable: true +``` + +### Start the observability stack + +Deploy the stack and verify it's running: + +```bash +# Start the stack +docker compose -f observability-stack.yml up -d + +# Verify Jaeger is running +curl http://localhost:16686/api/services + +# Verify Prometheus is running +curl http://localhost:9090/-/healthy + +# Verify the OpenTelemetry Collector is ready +curl -I http://localhost:8889/metrics +``` + +Access the interfaces: + +- **Jaeger UI**: `http://localhost:16686` +- **Prometheus Web UI**: `http://localhost:9090` +- **Grafana**: `http://localhost:3000` (login: admin/admin) + + + + +### Prerequisite + +If you've completed the [Kubernetes quickstart](../guides-k8s/quickstart.mdx), +skip to the next step. + +Otherwise, set up a local kind cluster and install the ToolHive operator: + +```bash +kind create cluster --name toolhive +helm upgrade -i toolhive-operator-crds oci://ghcr.io/stacklok/toolhive/toolhive-operator-crds +helm upgrade -i toolhive-operator oci://ghcr.io/stacklok/toolhive/toolhive-operator -n toolhive-system --create-namespace +``` + +Verify the operator is running: + +```bash +kubectl get pods -n toolhive-system +``` + +### Create the monitoring namespace + +Create a dedicated namespace for your observability stack: + +```bash +kubectl create namespace monitoring +``` + +### Deploy Jaeger + +Install Jaeger using Helm with a configuration suited for ToolHive: + +```bash +helm repo add jaegertracing https://jaegertracing.github.io/helm-charts +helm repo update +helm upgrade -i jaeger-all-in-one jaegertracing/jaeger -f https://raw.githubusercontent.com/stacklok/toolhive/refs/tags/v0.3.6/examples/otel/jaeger-values.yaml -n monitoring +``` + +### Deploy Prometheus and Grafana + +Install Prometheus and Grafana using the kube-prometheus-stack Helm chart: + +```bash +helm repo add prometheus-community https://prometheus-community.github.io/helm-charts +helm repo update +helm upgrade -i kube-prometheus-stack prometheus-community/kube-prometheus-stack -f https://raw.githubusercontent.com/stacklok/toolhive/v0.3.6/examples/otel/prometheus-stack-values.yaml -n monitoring +``` + +### Deploy OpenTelemetry Collector + +Create the collector configuration and deployment manifest: + +```bash +helm repo add open-telemetry https://open-telemetry.github.io/opentelemetry-helm-charts +helm repo update +helm upgrade -i otel-collector open-telemetry/opentelemetry-collector -f https://raw.githubusercontent.com/stacklok/toolhive/v0.3.6/examples/otel/otel-values.yaml -n monitoring +``` + +### Verify all components + +Verify all components are running: + +```bash +kubectl get pods -n monitoring +``` + +Wait for all pods to be in Running status before proceeding. The output should +look similar to: + +```text +NAME READY STATUS RESTARTS AGE +jaeger-all-in-one-6bf667c984-p5455 1/1 Running 0 2m12s +kube-prometheus-stack-grafana-69c88f77c5-b9f7m 3/3 Running 0 37s +kube-prometheus-stack-kube-state-metrics-55cb9c8889-cnlkt 1/1 Running 0 37s +kube-prometheus-stack-operator-85655fb7cd-rxms9 1/1 Running 0 37s +kube-prometheus-stack-prometheus-node-exporter-zzcvh 1/1 Running 0 37s +otel-collector-opentelemetry-collector-agent-hqtnq 1/1 Running 0 11s +prometheus-kube-prometheus-stack-prometheus-0 2/2 Running 0 36s +``` + + + + +## Step 2: Configure MCP server telemetry + +Now configure your MCP servers to send telemetry data to the observability +stack. + + + + +### Set global telemetry configuration + +Configure ToolHive CLI with default telemetry settings to send data to the +OpenTelemetry Collector: + +```bash +# Configure the OpenTelemetry endpoint (collector, not directly to Jaeger) +thv config otel set-endpoint localhost:4318 + +# Enable both metrics and tracing +thv config otel set-metrics-enabled true +thv config otel set-tracing-enabled true + +# Set 100% sampling for development +thv config otel set-sampling-rate 1.0 + +# Use insecure connection for local development +thv config otel set-insecure true +``` + +### Run an MCP server with telemetry + +Start an MCP server with enhanced telemetry configuration: + +```bash +thv run \ + --otel-service-name "mcp-fetch-server" \ + --otel-env-vars "USER,HOST" \ + --otel-enable-prometheus-metrics-path \ + fetch +``` + +Verify the server started and is exporting telemetry: + +```bash +# Check server status +thv list + +# Check Prometheus metrics are available on the MCP server +PORT=$(thv list | grep fetch | awk '{print $5}') +curl http://localhost:$PORT/metrics +``` + + + + +### Create an MCP server with telemetry + +Create an MCPServer resource with comprehensive telemetry configuration: + +```yaml {18-30} title="fetch-with-telemetry.yml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: fetch-telemetry + namespace: toolhive-system +spec: + image: ghcr.io/stackloklabs/gofetch/server + transport: streamable-http + proxyPort: 8080 + mcpPort: 8080 + resources: + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' + telemetry: + openTelemetry: + enabled: true + endpoint: otel-collector-opentelemetry-collector.monitoring.svc.cluster.local:4318 + serviceName: mcp-fetch-server + insecure: true # Using HTTP collector endpoint + metrics: + enabled: true + tracing: + enabled: true + samplingRate: '1.0' + prometheus: + enabled: true +``` + +Deploy the MCP server: + +```bash +kubectl apply -f fetch-with-telemetry.yml +``` + +Verify the MCP server is running and healthy: + +```bash +# Verify the server is running +kubectl get mcpserver -n toolhive-system + +# Check the pods are healthy +kubectl get pods -n toolhive-system -l app.kubernetes.io/instance=fetch-telemetry +``` + + + + +## Step 3: Generate telemetry data + +Create some MCP interactions to generate traces and metrics for analysis. + + + + +### Connect your AI client + +Your MCP server is already configured to work with your AI client from the CLI +quickstart. Simply use your client to make requests that will generate telemetry +data. + + + + +### Port-forward to access the MCP server + +In a separate terminal window, create a port-forward to connect your AI client: + +```bash +kubectl port-forward service/mcp-fetch-telemetry-proxy -n toolhive-system 8080:8080 +``` + +Leave this running for the duration of this tutorial. + +### Configure your AI client + +Use the ToolHive CLI to add the MCP server to your client configuration: + +```bash +thv run http://localhost:8080/mcp --name fetch-k8s --transport streamable-http +``` + + + + +### Generate sample data + +Make several requests using your AI client to create diverse telemetry: + +1. **Basic fetch request**: "Fetch the content from https://toolhive.dev and + summarize it" +2. **Multiple requests**: Make 3-4 more fetch requests with different URLs +3. **Error generation**: Try an invalid URL to generate error traces + +Each interaction creates rich telemetry data including: + +- Request traces with timing information sent to Jaeger +- Tool call details with sanitized arguments +- Performance metrics sent to Prometheus + +The CLI and Kubernetes deployments will both generate similar telemetry data, +with the Kubernetes setup including additional Kubernetes-specific attributes. + +## Step 4: Access and analyze telemetry data + +Now examine your telemetry data using Jaeger and Prometheus to understand MCP +server performance. + + + + +### Access Jaeger for traces + +Open Jaeger in your browser at `http://localhost:16686`. + +### Explore traces in Jaeger + +1. In the **Service** dropdown, select `mcp-fetch-server` +2. Click **Find Traces** to see recent traces +3. Click on individual traces to see detailed spans + +Look for traces with protocol and MCP-specific attributes like: + +```json +{ + "serviceName": "mcp-fetch-server", + "http.duration_ms": "307.8", + "http.status_code": 200, + "mcp.method": "tools/call", + "mcp.tool.name": "fetch", + "mcp.tool.arguments": "url=https://toolhive.dev", + "mcp.transport": "streamable-http", + "service.version": "v0.3.6" +} +``` + +### Access Grafana for visualization + +Open `http://localhost:3000` in your browser and log in using the default +credentials (`admin` / `admin`). + +### Import the ToolHive dashboard + +1. Click the **+** icon in the top-right of the Grafana interface and select + **Import dashboard** +2. In the **Import via dashboard JSON** model input box, paste the contents of + [this example dashboard file](https://raw.githubusercontent.com/stacklok/toolhive/main/examples/otel/grafana-dashboards/toolhive-cli-mcp-grafana-dashboard-otel-scrape.json) +3. Click **Load**, then **Import** + +Make some requests to your MCP server again and watch the dashboard update in +real-time. + +You can also explore other metrics in Grafana by creating custom panels and +queries. See the +[Observability guide](../concepts/observability.mdx#grafana-dashboard-queries) +for examples. + + + + +### Port-forward to Jaeger + +Access Jaeger through a port-forward: + +```bash +kubectl port-forward service/jaeger-all-in-one-query -n monitoring 16686:16686 +``` + +Open `http://localhost:16686` in your browser. + +### Explore traces in Jaeger + +1. In the **Service** dropdown, select `mcp-fetch-server` +2. Click **Find Traces** to see recent traces +3. Click on individual traces to see detailed spans + +Review the available information including MCP and Kubernetes-specific +attributes like: + +```json +{ + "serviceName": "mcp-fetch-server", + "http.duration_ms": "307.8", + "http.status_code": 200, + "mcp.method": "tools/call", + "mcp.tool.name": "fetch", + "mcp.tool.arguments": "url=https://toolhive.dev", + "mcp.transport": "streamable-http", + "k8s.deployment.name": "fetch-telemetry", + "k8s.namespace.name": "toolhive-system", + "k8s.node.name": "toolhive-control-plane", + "k8s.pod.name": "fetch-telemetry-7d7d55687c-glvpz", + "service.namespace": "toolhive-system", + "service.version": "v0.3.6" +} +``` + +### Port-forward to Grafana + +Access Grafana through a port-forward: + +```bash +kubectl port-forward service/kube-prometheus-stack-grafana -n monitoring 3000:80 +``` + +Open `http://localhost:3000` in your browser and log in using the default +credentials (`admin` / `admin`). + +### Import the ToolHive dashboard + +1. Click the **+** icon in the top-right of the Grafana interface and select + **Import dashboard** +2. In the **Import via dashboard JSON** model input box, paste the contents of + [this example dashboard file](https://raw.githubusercontent.com/stacklok/toolhive/main/examples/otel/grafana-dashboards/toolhive-mcp-grafana-dashboard-otel-scrape.json) +3. Click **Load**, then **Import** + +Make some requests to your MCP server again and watch the dashboard update in +real-time. + +You can also explore other metrics in Grafana by creating custom panels and +queries. See the +[Observability guide](../concepts/observability.mdx#grafana-dashboard-queries) +for examples. + + + + +## Step 5: Cleanup + +When you're finished exploring, clean up your resources. + + + + +### Stop MCP servers + +```bash +# Stop and remove the MCP server +thv rm fetch + +# Clear telemetry configuration (optional) +thv config otel unset-endpoint +thv config otel unset-metrics-enabled +thv config otel unset-tracing-enabled +thv config otel unset-sampling-rate +thv config otel unset-insecure +``` + +### Stop observability stack + +```bash +# Stop all containers +docker compose -f observability-stack.yml down + +# Remove all data (optional) +docker compose -f observability-stack.yml down -v + +# Clean up provisioning directories (optional) +rm -rf grafana/ +``` + + + + +### Remove MCP servers + +```bash +# Delete the MCP server +kubectl delete mcpserver fetch-telemetry -n toolhive-system +``` + +### Remove observability stack + +```bash +# Delete observability components +helm uninstall otel-collector -n monitoring +helm uninstall kube-prometheus-stack -n monitoring +helm uninstall jaeger-all-in-one -n monitoring + +# Remove the monitoring namespace +kubectl delete namespace monitoring +``` + +### Optional: Remove the kind cluster + +If you're completely done: + +```bash +kind delete cluster --name toolhive +``` + + + + +## What's next? + +Congratulations! You've successfully set up comprehensive observability for +ToolHive MCP workloads using Jaeger and Prometheus. + +To learn more about ToolHive's telemetry capabilities and best practices, see +the [Observability concepts guide](../concepts/observability.mdx). + +Here are some next steps to explore: + +- **Custom dashboards**: Create Grafana dashboards that query both Jaeger and + Prometheus +- **Alerting**: Set up Prometheus AlertManager for performance and error alerts +- **Performance optimization**: Use telemetry data to optimize MCP server + performance +- **Distributed tracing**: Understand request flows across multiple MCP servers + + + + +### CLI-specific next steps + +- **Review the CLI telemetry guide**: Explore + [detailed configuration options](../guides-cli/telemetry-and-metrics.mdx) +- **Scale to multiple servers**: Run multiple MCP servers with different + configurations +- **Production CLI setup**: Learn about + [secrets management](../guides-cli/secrets-management.mdx) and + [custom permissions](../guides-cli/custom-permissions.mdx) +- **Alternative backends**: Try other observability platforms mentioned in the + [CLI telemetry guide](../guides-cli/telemetry-and-metrics.mdx) + + + + +### Kubernetes-specific next steps + +- **Review the Kubernetes telemetry guide**: Explore + [detailed configuration options](../guides-k8s/telemetry-and-metrics.mdx) +- **Production deployment**: Set up production-grade + [Jaeger](https://www.jaegertracing.io/docs/deployment/) and + [Prometheus](https://prometheus.io/docs/prometheus/latest/installation/) with + persistent storage, or configure an OpenTelemetry Collector to work with your + existing observability tools +- **Advanced MCP configurations**: Explore + [Kubernetes MCP deployment patterns](../guides-k8s/run-mcp-k8s.mdx) +- **Secrets integration**: Learn about + [HashiCorp Vault integration](./vault.mdx) +- **Service mesh observability**: Integrate with Istio or Linkerd for enhanced + tracing + + + + +## Related information + +- [Observability concepts](../concepts/observability.mdx) - Understanding + ToolHive's telemetry architecture +- [CLI telemetry guide](../guides-cli/telemetry-and-metrics.mdx) - Detailed CLI + configuration options +- [Kubernetes telemetry guide](../guides-k8s/telemetry-and-metrics.mdx) - + Kubernetes operator telemetry features +- [OpenTelemetry Collector documentation](https://opentelemetry.io/docs/collector/) - + Official OpenTelemetry Collector documentation +- [Jaeger documentation](https://www.jaegertracing.io/docs/) - Official Jaeger + documentation +- [Prometheus documentation](https://prometheus.io/docs/) - Official Prometheus + documentation + +## Troubleshooting + + + + +
+Docker containers won't start + +Check Docker daemon and container logs: + +```bash +# Verify Docker is running +docker info + +# Check container logs +docker compose -f observability-stack.yml logs jaeger +docker compose -f observability-stack.yml logs prometheus +docker compose -f observability-stack.yml logs otel-collector +``` + +Common issues: + +- Port conflicts with existing services +- Insufficient Docker memory allocation +- Missing configuration files + +
+ +
+ToolHive CLI not sending telemetry + +Verify telemetry configuration: + +```bash +# Check current config +thv config otel get-endpoint +thv config otel get-metrics-enabled + +``` + +Check the ToolHive proxy logs for telemetry export errors: + +```bash +thv logs fetch --proxy [--follow] +``` + +Alternatively, you can check the log file directly at: + +- **macOS**: `~/Library/Application Support/toolhive/logs/fetch.log` +- **Windows**: `%LOCALAPPDATA%\toolhive\logs\fetch.log` +- **Linux**: `~/.local/share/toolhive/logs/fetch.log` + +
+ +
+No traces in Jaeger + +Check the telemetry pipeline: + +1. **Verify collector is receiving data**: `curl http://localhost:8888/metrics` +2. **Check collector logs**: `docker logs otel-collector` +3. **Verify Jaeger connectivity**: `curl http://localhost:16686/api/services` + +
+ +
+ + +
+Pods stuck in pending state + +Check cluster resources and pod events: + +```bash +# Check pod status +kubectl get pods -n monitoring + +# Describe problematic pods +kubectl describe pod -n monitoring + +# Check node resources +kubectl top nodes +``` + +Common issues: + +- Insufficient cluster resources +- Image pull failures +- Network policies blocking communication + +
+ +
+MCP server not sending telemetry + +Verify the telemetry configuration and connectivity: + +```bash +# Check MCPServer status +kubectl describe mcpserver fetch-telemetry -n toolhive-system + +# Check OpenTelemetry Collector logs +kubectl logs deployment/otel-collector -n monitoring + +# Verify service connectivity +kubectl exec -it deployment/otel-collector -n monitoring -- wget -qO- http://jaeger:16686/api/services +``` + +
+ +
+
+ +
+No metrics in Prometheus + +Common troubleshooting steps: + +1. **Verify Prometheus targets**: Check `http://localhost:9090/targets` to + ensure `otel-collector` target is UP +2. **Check collector metrics endpoint**: `curl http://localhost:8889/metrics` + (CLI) or port-forward and check in K8s +3. **Review collector configuration**: Ensure the Prometheus exporter is + properly configured +4. **Check Prometheus config**: Verify the scrape configuration includes the + collector endpoint + +
diff --git a/versioned_docs/version-1.1/toolhive/integrations/vault.mdx b/versioned_docs/version-1.1/toolhive/integrations/vault.mdx new file mode 100644 index 00000000..56524ff1 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/integrations/vault.mdx @@ -0,0 +1,285 @@ +--- +title: HashiCorp Vault integration +description: + Learn how to securely manage MCP server secrets using HashiCorp Vault with + ToolHive Kubernetes Operator. +--- + +This tutorial shows how to integrate HashiCorp Vault with the ToolHive +Kubernetes Operator to securely manage secrets for your MCP servers. Using the +[Vault Agent Injector](https://developer.hashicorp.com/vault/docs/deploy/kubernetes/injector), +you can automatically provision secrets into MCP server pods without exposing +sensitive data in your Kubernetes manifests. + +To demonstrate this integration, you will deploy a GitHub MCP server that +retrieves a GitHub personal access token (PAT) from Vault. + +:::info[Prerequisites] + +Before starting this tutorial, ensure you have: + +- A Kubernetes cluster with the ToolHive Operator installed +- kubectl configured to access your cluster +- Helm 3.x installed +- Basic familiarity with HashiCorp Vault concepts +- A GitHub Personal Access Token (PAT) + +If you need help installing the ToolHive Operator, see the +[Kubernetes quickstart guide](../guides-k8s/quickstart.mdx). + +::: + +## Overview + +The integration works by using HashiCorp Vault's Agent Injector to automatically +inject secrets into MCP server pods. When you add specific annotations to your +MCPServer resource, the Vault Agent Injector: + +1. Detects the annotations and injects a Vault Agent sidecar +2. Authenticates with Vault using Kubernetes service account tokens of the + `proxyrunner` pod +3. Retrieves secrets from Vault and writes them to a shared volume +4. Makes the secrets available as environment variables to your MCP server pod + +## Step 1: Install and configure Vault + +First, install Vault with the Agent Injector enabled in your Kubernetes cluster. + +### Install Vault using Helm + +Add the HashiCorp Helm repository and install Vault: + +```bash +# Add HashiCorp Helm repository +helm repo add hashicorp https://helm.releases.hashicorp.com +helm repo update + +# Create vault namespace +kubectl create namespace vault + +# Install Vault with Agent Injector +helm install vault hashicorp/vault \ + --namespace vault \ + --set "server.dev.enabled=true" \ + --set "server.dev.devRootToken=dev-only-token" \ + --set "injector.enabled=true" +``` + +:::warning[Development setup only] + +This tutorial uses Vault in development mode (`server.dev.enabled=true`) with a +static root token for simplicity. **Do not use this configuration in +production**. For production deployments, follow the [Vault production hardening +guide][vault-hardening]. + +::: + +Wait for the Vault pod to be ready: + +```bash +kubectl wait --for=condition=ready pod vault-0 \ + --namespace vault \ + --timeout=300s +``` + +### Configure Vault authentication + +Configure Vault to authenticate Kubernetes service accounts: + +```bash +# Get the Vault pod name +VAULT_POD=$(kubectl get pods --namespace vault \ + -l app.kubernetes.io/name=vault \ + -o jsonpath="{.items[0].metadata.name}") + +# Enable Kubernetes auth method +kubectl exec --namespace vault "$VAULT_POD" -- \ + vault auth enable kubernetes + +# Configure Kubernetes auth +kubectl exec --namespace vault "$VAULT_POD" -- \ + vault write auth/kubernetes/config \ + kubernetes_host="https://kubernetes.default.svc:443" \ + kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt \ + token_reviewer_jwt=@/var/run/secrets/kubernetes.io/serviceaccount/token +``` + +### Set up secrets engine and policies + +Enable a key-value secrets engine and create the necessary policies: + +```bash +# Enable KV secrets engine +kubectl exec --namespace vault "$VAULT_POD" -- \ + vault secrets enable -path=workload-secrets kv-v2 + +# Create Vault policy for MCP workloads +kubectl exec --namespace vault "$VAULT_POD" -- \ + sh -c 'vault policy write toolhive-workload-secrets - << EOF +path "auth/token/lookup-self" { capabilities = ["read"] } +path "auth/token/renew-self" { capabilities = ["update"] } +path "workload-secrets/data/github-mcp/*" { capabilities = ["read"] } +EOF' + +# Create Kubernetes auth role +kubectl exec --namespace vault "$VAULT_POD" -- \ + vault write auth/kubernetes/role/toolhive-mcp-workloads \ + bound_service_account_names="*-proxy-runner,mcp-*" \ + bound_service_account_namespaces="toolhive-system" \ + policies="toolhive-workload-secrets" \ + audience="https://kubernetes.default.svc.cluster.local" \ + ttl="1h" \ + max_ttl="4h" +``` + +## Step 2: Store secrets in Vault + +Create secrets for your MCP servers in Vault. This example shows how to store a +GitHub personal access token: + +```bash +# Store GitHub MCP server configuration +kubectl exec --namespace vault "$VAULT_POD" -- \ + vault kv put workload-secrets/github-mcp/config \ + token="ghp_your_github_token_here" \ + organization="your-org" +``` + +You can verify the secret was stored correctly: + +```bash +kubectl exec --namespace vault "$VAULT_POD" -- \ + vault kv get workload-secrets/github-mcp/config +``` + +## Step 3: Configure your MCPServer resource + +Create an MCPServer resource with Vault annotations to enable automatic secret +injection. The key is using the `podTemplateMetadataOverrides` field to add +annotations to the proxy runner pods: + +```yaml title="github-mcp-with-vault.yaml" +apiVersion: toolhive.stacklok.dev/v1alpha1 +kind: MCPServer +metadata: + name: github-vault + namespace: toolhive-system +spec: + image: ghcr.io/github/github-mcp-server:latest + transport: stdio + proxyPort: 9095 + resources: + limits: + cpu: '100m' + memory: '128Mi' + requests: + cpu: '50m' + memory: '64Mi' + resourceOverrides: + proxyDeployment: + podTemplateMetadataOverrides: + annotations: + # Enable Vault Agent injection + vault.hashicorp.com/agent-inject: 'true' + vault.hashicorp.com/role: 'toolhive-mcp-workloads' + + # Inject GitHub configuration secret + vault.hashicorp.com/agent-inject-secret-github-config: 'workload-secrets/data/github-mcp/config' + vault.hashicorp.com/agent-inject-template-github-config: | + {{- with secret "workload-secrets/data/github-mcp/config" -}} + GITHUB_PERSONAL_ACCESS_TOKEN={{ .Data.data.token }} + {{- end -}} +``` + +### Understanding the annotations + +The key annotations that enable Vault integration are: + +- `vault.hashicorp.com/agent-inject: "true"` - Enables Vault Agent injection for + this pod +- `vault.hashicorp.com/role: "toolhive-mcp-workloads"` - Specifies the Vault + role to use for authentication +- `vault.hashicorp.com/agent-inject-secret-github-config` - Tells Vault to + retrieve a secret and make it available as a file +- `vault.hashicorp.com/agent-inject-template-github-config` - Uses a Vault + template to format the secret as environment variables + +When ToolHive detects the `vault.hashicorp.com/agent-inject` annotation, it +automatically configures the proxy runner to read environment variables from the +`/vault/secrets/` directory where the Vault Agent writes the rendered templates. + +## Step 4: Deploy your MCPServer + +Apply your MCPServer configuration: + +```bash +kubectl apply -f github-mcp-with-vault.yaml +``` + +Monitor the deployment to ensure both the Vault Agent and ToolHive proxy runner +start successfully: + +```bash +# Watch the pod start up +kubectl get pods -n toolhive-system -w + +# Get the pod name +POD_NAME=$(kubectl get pods -n toolhive-system \ + -l app.kubernetes.io/instance=github-vault \ + -o jsonpath="{.items[0].metadata.name}") + +# Check pod logs +kubectl logs -n toolhive-system $POD_NAME -c vault-agent +kubectl logs -n toolhive-system $POD_NAME -c toolhive +``` + +You should see the Vault Agent successfully authenticate and retrieve secrets, +and the ToolHive proxy runner start with the injected environment variables. + +## Step 5: Verify the integration + +Test that your MCP server has access to the secrets by checking the running pod: + +```bash +# Get the proxy pod name - note the instance name is the same +# as the name of our MCPServer +PROXY_POD_NAME=$(kubectl get pods -n toolhive-system \ + -l app.kubernetes.io/instance=github-vault \ + -o jsonpath="{.items[0].metadata.name}") + +# Get the mcp server pod name - note the instance name is the same +# as the name of our MCPServer +MCP_POD_NAME=$(kubectl get pods -ntoolhive-system \ + -lapp=github-vault,toolhive-tool-type=mcp \ + -ojsonpath='{.items[0].metadata.name}') + +# Verify the Vault Agent wrote the secret file +kubectl exec -n toolhive-system "$PROXY_POD_NAME" -c vault-agent -- \ + cat /vault/secrets/github-config + +# Check that the environment variable is available to the MCP server +kubectl get pod $MCP_POD_NAME -n toolhive-system -o jsonpath='{range .spec.containers[?(@.name=="mcp")].env[*]}{.name}{"="}{.value}{"\n"}{end}' +``` + +## Security best practices + +:::tip[Production recommendations] + +- Use Vault in production mode with proper TLS certificates +- Implement least-privilege policies for secret access +- Enable audit logging in Vault +- Regularly rotate Vault tokens and secrets +- Monitor Vault Agent logs for authentication issues +- Use namespace isolation for different environments + +::: + +## Related information + +- [Kubernetes quickstart guide](../guides-k8s/quickstart.mdx) +- [Secrets management guide](../guides-cli/secrets-management.mdx) +- [HashiCorp Vault documentation](https://developer.hashicorp.com/vault/docs) +- [Vault Agent Injector documentation][vault-injector] + +[vault-hardening]: https://developer.hashicorp.com/vault/tutorials/operations/production-hardening +[vault-injector]: https://developer.hashicorp.com/vault/docs/platform/k8s/injector diff --git a/versioned_docs/version-1.1/toolhive/reference/api.mdx b/versioned_docs/version-1.1/toolhive/reference/api.mdx new file mode 100644 index 00000000..a06d6ee2 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/api.mdx @@ -0,0 +1,9 @@ +--- +title: API reference +description: Reference documentation for ToolHive's REST API +hide_table_of_contents: true +--- + +import ApiDocMdx from '@theme/ApiDocMdx'; + + diff --git a/versioned_docs/version-1.1/toolhive/reference/authz-policy-reference.mdx b/versioned_docs/version-1.1/toolhive/reference/authz-policy-reference.mdx new file mode 100644 index 00000000..4429debf --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/authz-policy-reference.mdx @@ -0,0 +1,495 @@ +--- +title: Authorization policy reference +description: Complete reference for Cedar entity types, actions, attributes, and + annotations available when writing ToolHive authorization policies. +--- + +This page lists the Cedar entity types, actions, attributes, and annotations +available when writing authorization policies for ToolHive MCP servers. It also +covers group membership and the HTTP PDP model for external policy decision +points. + +For conceptual guidance and practical examples, see +[Cedar policies](../concepts/cedar-policies.mdx). + +## Cedar entity types + +Every Cedar authorization request involves three entity types: a principal, an +action, and a resource. ToolHive maps MCP concepts to these Cedar entities +automatically. + +| Entity type | Format | Description | +| ------------- | ----------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `Client` | `Client::""` | The authenticated user, identified by the `sub` claim from the access token | +| `Action` | `Action::""` | The MCP operation being performed | +| `Tool` | `Tool::""` | A tool resource (used for `tools/call`) | +| `Prompt` | `Prompt::""` | A prompt resource (used for `prompts/get`) | +| `Resource` | `Resource::""` | A data resource (used for `resources/read`). The URI is [sanitized](#resource-uri-sanitization) for Cedar compatibility | +| `FeatureType` | `FeatureType::""` | A feature category entity. Values: `tool`, `prompt`, `resource`. Not currently used for authorization; list operations are handled via [response filtering](#list-operation-filtering) | +| `THVGroup` | `THVGroup::""` | A group membership entity. Used with Cedar's `in` operator for [group-based policies](#group-membership) | + +## Cedar actions + +ToolHive maps MCP methods to Cedar actions. Each action corresponds to a +specific MCP operation. + +### Actions that require authorization + +These actions are evaluated against your Cedar policies: + +| Action | MCP method | Description | +| ------------------------- | ---------------- | ----------------------------- | +| `Action::"call_tool"` | `tools/call` | Call a specific tool | +| `Action::"get_prompt"` | `prompts/get` | Retrieve a specific prompt | +| `Action::"read_resource"` | `resources/read` | Read a specific data resource | + +### List operations + +List methods (`tools/list`, `prompts/list`, `resources/list`) bypass +request-level authorization entirely. ToolHive allows the list request through +and filters the response to include only items the caller is authorized to +access using the individual-access actions above. See +[List operation filtering](#list-operation-filtering) for details. + +### Always-allowed MCP methods + +These MCP methods bypass authorization entirely. You cannot write policies to +restrict them: + +| MCP method | Purpose | +| --------------------- | ---------------------------------- | +| `initialize` | Protocol initialization handshake | +| `ping` | Health check | +| `features/list` | Capability discovery | +| `roots/list` | Root directory discovery | +| `logging/setLevel` | Client logging preference | +| `completion/complete` | Argument auto-completion | +| `notifications/*` | All server-to-client notifications | + +### Denied-by-default MCP methods + +These MCP methods are not in the authorization map and are always denied. They +require new authorization features before they can be enabled: + +- `elicitation/create` -- User input prompting +- `sampling/createMessage` -- LLM text generation +- `tasks/list`, `tasks/get`, `tasks/cancel`, `tasks/result` -- Task management + +## Principal attributes + +The principal entity (`Client::`) receives all JWT claims from the access token +with a `claim_` prefix. Any claim in the token becomes an attribute you can +reference in policies. + +### Common principal attributes + +| Attribute | Source | Cedar type | Description | +| -------------- | ------------ | -------------- | ----------------------------------------------------------------- | +| `claim_sub` | JWT `sub` | String | Subject identifier (also used as the entity ID) | +| `claim_name` | JWT `name` | String | Display name | +| `claim_email` | JWT `email` | String | Email address | +| `claim_roles` | JWT `roles` | Set of Strings | Role memberships | +| `claim_groups` | JWT `groups` | Set of Strings | Group memberships | +| `claim_role` | JWT `role` | String | Single role (some identity providers use this instead of `roles`) | +| `claim_` | JWT `` | Varies | Any other JWT claim | + +:::tip + +The exact attributes available depend on your identity provider and token +configuration. Check your access token's claims to see what's available. Every +claim becomes a `claim_`-prefixed attribute automatically. + +::: + +### Claim type mapping + +| JWT claim type | Cedar type | +| -------------------- | ------------------------------------------- | +| String | `String` | +| Boolean | `Bool` | +| Integer | `Long` | +| Float | `Decimal` | +| Array of strings | `Set` of `String` values | +| Array of mixed types | `Set` (each element converted individually) | + +## Resource attributes + +Resource attributes vary depending on the type of MCP operation. Each operation +type provides a different set of attributes on the resource entity. + +### Tool call attributes (`tools/call`) + +When a client calls a tool, the resource entity (`Tool::`) has these attributes: + +| Attribute | Type | Description | +| ----------------- | ------ | ------------------------------------------------------------------------------- | +| `name` | String | The tool name | +| `operation` | String | Always `"call"` | +| `feature` | String | Always `"tool"` | +| `readOnlyHint` | Bool | From [tool annotations](#tool-annotation-attributes), if the MCP server sets it | +| `destructiveHint` | Bool | From tool annotations, if set | +| `idempotentHint` | Bool | From tool annotations, if set | +| `openWorldHint` | Bool | From tool annotations, if set | +| `arg_` | Varies | Tool argument values (see [argument preprocessing](#argument-preprocessing)) | + +### Prompt get attributes (`prompts/get`) + +When a client retrieves a prompt, the resource entity (`Prompt::`) has these +attributes: + +| Attribute | Type | Description | +| ----------- | ------ | ---------------------- | +| `name` | String | The prompt name | +| `operation` | String | Always `"get"` | +| `feature` | String | Always `"prompt"` | +| `arg_` | Varies | Prompt argument values | + +### Resource read attributes (`resources/read`) + +When a client reads a data resource, the resource entity (`Resource::`) has +these attributes: + +| Attribute | Type | Description | +| ----------- | ------ | ----------------------------------------- | +| `name` | String | The sanitized URI (same as the entity ID) | +| `uri` | String | The original, unsanitized resource URI | +| `operation` | String | Always `"read"` | +| `feature` | String | Always `"resource"` | +| `arg_` | Varies | Request argument values | + +### Feature list attributes (list operations) + +:::info[Not currently used] + +The `FeatureType` entity and its attributes are defined in the Cedar authorizer +but are not evaluated during normal request processing. List operations bypass +request-level authorization and use +[response filtering](#list-operation-filtering) instead. This section is +included for completeness. + +::: + +When a client lists tools, prompts, or resources, the `FeatureType::` entity has +these attributes: + +| Attribute | Type | Description | +| ----------- | ------ | ------------------------------------------------------- | +| `name` | String | The feature type (same as the entity ID) | +| `type` | String | The feature type: `"tool"`, `"prompt"`, or `"resource"` | +| `operation` | String | Always `"list"` | +| `feature` | String | The feature type | + +## Tool annotation attributes + +MCP servers can declare behavioral hints on their tools through +[annotations](https://modelcontextprotocol.io/docs/specification/2025-06-18/server/tools#annotations). +ToolHive caches these annotations from `tools/list` responses and makes them +available as resource attributes during `tools/call` authorization. + +| Attribute | Type | Meaning when `true` | Meaning when `false` | +| ----------------- | ---- | -------------------------------------------------------------------------------- | -------------------------------------------------------------- | +| `readOnlyHint` | Bool | The tool only reads data; it does not modify anything | The tool may modify data | +| `destructiveHint` | Bool | The tool may perform destructive or irreversible operations | The tool's modifications are non-destructive or reversible | +| `idempotentHint` | Bool | Calling the tool multiple times with the same arguments produces the same result | Repeated calls may have different effects | +| `openWorldHint` | Bool | The tool interacts with external systems outside the MCP server's control | The tool operates only within a closed, controlled environment | + +:::warning[Annotations may be absent] + +Not all MCP servers set all annotation fields. An annotation attribute is only +present on the resource entity when the MCP server explicitly sets it. If a tool +omits an annotation, that attribute does not exist on the entity. + +Always use Cedar's `has` operator to check for the presence of an annotation +before accessing its value. Without `has`, accessing a missing attribute causes +a Cedar evaluation error, which ToolHive treats as a deny. + +::: + +### The `has` operator + +The `has` operator is essential for writing safe annotation-based policies. It +checks whether an attribute exists on an entity before you try to read it: + +```text +// Safe: checks existence before access +permit( + principal, + action == Action::"call_tool", + resource +) when { + resource has readOnlyHint && resource.readOnlyHint == true +}; +``` + +```text +// Unsafe: fails with an evaluation error if readOnlyHint is absent +permit( + principal, + action == Action::"call_tool", + resource +) when { + resource.readOnlyHint == true +}; +``` + +### Trust boundary + +Annotations are sourced exclusively from the MCP server's `tools/list` response, +not from the client's `tools/call` request. This prevents a malicious client +from setting `readOnlyHint: true` on a destructive tool to bypass +annotation-based policies. + +## Context attributes + +The Cedar context record contains a merged copy of all JWT claims and tool +arguments. This gives you an alternative way to reference these values in +policies. Context attributes use the same prefixes as entity attributes: + +| Prefix | Source | Example | +| ------------- | --------------------- | -------------------------------------------- | +| `claim_` | JWT claims | `context.claim_email == "admin@example.com"` | +| `arg_` | Tool/prompt arguments | `context.arg_location == "New York"` | + +You can use either entity attributes or context attributes in your policies. +Both contain the same values: + +```text +// These are equivalent: +principal.claim_roles.contains("admin") +context.claim_roles.contains("admin") + +// These are also equivalent: +resource.arg_location == "New York" +context.arg_location == "New York" +``` + +## Group membership + +ToolHive automatically extracts group claims from JWT tokens and creates +`THVGroup` parent entities for the principal. This lets you write group-based +policies using Cedar's `in` operator. + +### How groups are resolved + +ToolHive checks the following JWT claim names in order and uses the first one +found: + +1. Custom claim name (if configured via `group_claim_name` in Cedar config) +2. `groups` -- Microsoft Entra ID, Okta, Auth0, PingIdentity +3. `roles` -- Keycloak +4. `cognito:groups` -- AWS Cognito + +The claim value must be an array of strings. Each string becomes a `THVGroup` +entity, and the principal is added as a child of each group. + +### Group policy examples + +```text +// Allow members of the "engineering" group to call any tool +permit( + principal in THVGroup::"engineering", + action == Action::"call_tool", + resource +); +``` + +```text +// Allow only the "platform" group to read infrastructure resources +permit( + principal in THVGroup::"platform", + action == Action::"read_resource", + resource +); +``` + +### Configuring a custom group claim + +If your identity provider uses a non-standard claim name for groups (for +example, Auth0 namespaced claims), configure it in the Cedar authorization +config: + +```yaml title="authz-config.yaml" +version: '1.0' +type: cedarv1 +cedar: + group_claim_name: 'https://example.com/groups' + policies: + - 'permit(principal in THVGroup::"admins", action, resource);' + entities_json: '[]' +``` + +## Argument preprocessing + +Tool and prompt arguments are converted to Cedar-compatible types with an `arg_` +prefix. The conversion rules depend on the argument's Go type: + +| Argument type | Cedar attribute | Cedar type | Example | +| ----------------------- | ------------------- | -------------------- | ------------------------------------- | +| String | `arg_` | String | `resource.arg_location == "NYC"` | +| Boolean | `arg_` | Bool | `resource.arg_verbose == true` | +| Integer | `arg_` | Long | `resource.arg_limit == 10` | +| Float | `arg_` | Decimal | `resource.arg_threshold == 0.95` | +| Complex (object, array) | `arg__present` | Bool (always `true`) | `resource.arg_config_present == true` | + +Complex argument types (objects, nested arrays) cannot be represented directly +in Cedar. Instead, ToolHive creates a boolean `arg__present` attribute set +to `true`, which lets you check whether the argument was provided without +inspecting its value. + +## Resource URI sanitization + +For `resources/read` operations, the resource URI is sanitized to create a valid +Cedar entity ID. The following characters are replaced with underscores (`_`): +`:`, `/`, `\`, `?`, `&`, `=`, `#`, `.`, and ` ` (space). + +For example, the URI `file:///data/config.json` becomes the entity ID +`Resource::"file____data_config_json"`. + +To write policies against resource URIs, use the unsanitized `uri` attribute +instead of matching the entity ID directly: + +```text +// Use the uri attribute for readable policies +permit( + principal, + action == Action::"read_resource", + resource +) when { + resource.uri == "file:///data/config.json" +}; +``` + +## List operation filtering + +List operations (`tools/list`, `prompts/list`, `resources/list`) bypass +request-level authorization entirely. ToolHive forwards the list request to the +MCP server, then filters the response to include only items the caller is +authorized to access. + +For each item in the list response, ToolHive runs a policy check using the +corresponding individual-access action: + +| List method | Per-item check uses | +| ---------------- | -------------------------------------------------------------------- | +| `tools/list` | `Action::"call_tool"` against each `Tool::""` | +| `prompts/list` | `Action::"get_prompt"` against each `Prompt::""` | +| `resources/list` | `Action::"read_resource"` against each `Resource::""` | + +This means you don't need separate list policies. Your `call_tool`, +`get_prompt`, and `read_resource` policies automatically control what appears in +list responses. For resources, the per-item check uses the +[sanitized](#resource-uri-sanitization) entity ID, while the original URI +remains available via the `resource.uri` attribute. + +:::note + +Because list responses are filtered using `call_tool`, `get_prompt`, and +`read_resource` policies, an item only appears in a list response when the +corresponding individual-access policy permits it. For example, if no +`call_tool` policy permits a given tool, that tool won't appear in `tools/list` +responses. + +::: + +## Custom static entities + +You can define custom entities with arbitrary attributes using the +`entities_json` field in your Cedar configuration. These entities are merged +with the dynamically created entities at evaluation time. This lets you attach +metadata to tools, prompts, or resources that you can reference in policies. + +```yaml title="authz-config.yaml" +version: '1.0' +type: cedarv1 +cedar: + policies: + - | + permit( + principal, + action == Action::"call_tool", + resource + ) when { + resource.owner == principal.claim_sub + }; + entities_json: | + [ + { + "uid": "Tool::weather", + "attrs": { + "owner": "user123", + "department": "engineering" + } + }, + { + "uid": "Tool::billing", + "attrs": { + "owner": "finance-bot", + "department": "finance" + } + } + ] +``` + +:::warning + +Static entity attributes are merged with dynamic attributes at evaluation time. +The dynamic attributes (`name`, `operation`, `feature`, and any `arg_` or +annotation attributes) always take precedence over static ones. Don't define +static attributes using reserved names. + +::: + +## HTTP PDP PORC mapping + +The HTTP PDP authorizer (`httpv1`) maps MCP requests to a PORC +(Principal-Operation-Resource-Context) model for external policy decision +points. + +### PORC fields + +| Field | Format | Example | +| ----------------------------------------- | -------------------------------------------------- | --------------------------------- | +| `principal.sub` | JWT `sub` claim | `"user@example.com"` | +| `principal.roles` or `principal.mroles` | Depends on [claim mapper](#http-pdp-claim-mappers) | `["developer"]` | +| `principal.groups` or `principal.mgroups` | Depends on claim mapper | `["engineering"]` | +| `principal.scopes` | JWT `scope` or `scopes` | `["read", "write"]` | +| `operation` | `mcp::` | `"mcp:tool:call"` | +| `resource` | `mrn:mcp:::` | `"mrn:mcp:myserver:tool:weather"` | + +### PORC context fields + +Context fields are optional and controlled by the `context` configuration: + +| Field | Config required | Description | +| ----------------------------------------- | ----------------------------- | --------------------- | +| `context.mcp.feature` | `include_operation: true` | MCP feature type | +| `context.mcp.operation` | `include_operation: true` | MCP operation type | +| `context.mcp.resource_id` | `include_operation: true` | Resource identifier | +| `context.mcp.args.` | `include_args: true` | Tool/prompt arguments | +| `context.mcp.annotations.readOnlyHint` | Automatic for tool operations | Tool annotation hint | +| `context.mcp.annotations.destructiveHint` | Automatic for tool operations | Tool annotation hint | +| `context.mcp.annotations.idempotentHint` | Automatic for tool operations | Tool annotation hint | +| `context.mcp.annotations.openWorldHint` | Automatic for tool operations | Tool annotation hint | + +### HTTP PDP claim mappers + +| Mapper | Config value | Principal fields | Compatible with | +| ------------- | --------------------------- | ------------------------------------------------------------------ | ------------------------------------------- | +| MPE | `claim_mapping: "mpe"` | `sub`, `mroles`, `mgroups`, `scopes`, `mclearance`, `mannotations` | Manetu PolicyEngine | +| Standard OIDC | `claim_mapping: "standard"` | `sub`, `roles`, `groups`, `scopes` | Generic PDPs expecting standard OIDC claims | + +## Next steps + +- Learn practical policy patterns and profiles in + [Cedar policies](../concepts/cedar-policies.mdx) +- Set up authorization for [CLI-managed MCP servers](../guides-cli/auth.mdx) or + [Kubernetes-deployed MCP servers](../guides-k8s/auth-k8s.mdx) +- Follow the end-to-end + [Role-based authorization with Okta](../integrations/okta.mdx) tutorial + +## Related information + +- [Authentication and authorization](../concepts/auth-framework.mdx) -- Overview + of the authentication and authorization framework +- [Cedar documentation](https://docs.cedarpolicy.com/) -- Official Cedar policy + language reference diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv.md new file mode 100644 index 00000000..63c348cf --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv.md @@ -0,0 +1,59 @@ +--- +title: thv +hide_title: true +description: Reference for ToolHive CLI command `thv` +last_update: + author: autogenerated +slug: thv +mdx: + format: md +--- + +## thv + +ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers + +### Synopsis + +ToolHive (thv) is a lightweight, secure, and fast manager for MCP (Model Context Protocol) servers. +It is written in Go and has extensive test coverage—including input validation—to ensure reliability and security. + +Under the hood, ToolHive acts as a very thin client for the Docker/Podman/Colima Unix socket API. +This design choice allows it to remain both efficient and lightweight while still providing powerful, +container-based isolation for running MCP servers. + +``` +thv [flags] +``` + +### Options + +``` + --debug Enable debug mode + -h, --help help for thv +``` + +### SEE ALSO + +- [thv build](thv_build.md) - Build a container for an MCP server without running it +- [thv client](thv_client.md) - Manage MCP clients +- [thv config](thv_config.md) - Manage application configuration +- [thv export](thv_export.md) - Export a workload's run configuration to a file +- [thv group](thv_group.md) - Manage logical groupings of MCP servers +- [thv inspector](thv_inspector.md) - Launches the MCP Inspector UI and connects it to the specified MCP server +- [thv list](thv_list.md) - List running MCP servers +- [thv logs](thv_logs.md) - Output the logs of an MCP server or manage log files +- [thv mcp](thv_mcp.md) - Interact with MCP servers for debugging +- [thv proxy](thv_proxy.md) - Create a transparent proxy for an MCP server with authentication support +- [thv registry](thv_registry.md) - Manage MCP server registry +- [thv rm](thv_rm.md) - Remove one or more MCP servers +- [thv run](thv_run.md) - Run an MCP server +- [thv runtime](thv_runtime.md) - Commands related to the container runtime +- [thv search](thv_search.md) - Search for MCP servers +- [thv secret](thv_secret.md) - Manage secrets +- [thv serve](thv_serve.md) - Start the ToolHive API server +- [thv skill](thv_skill.md) - Manage skills +- [thv start](thv_start.md) - Start (resume) a tooling server +- [thv status](thv_status.md) - Show detailed status of an MCP server +- [thv stop](thv_stop.md) - Stop one or more MCP servers +- [thv version](thv_version.md) - Show the version of ToolHive diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_build.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_build.md new file mode 100644 index 00000000..149b6c70 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_build.md @@ -0,0 +1,70 @@ +--- +title: thv build +hide_title: true +description: Reference for ToolHive CLI command `thv build` +last_update: + author: autogenerated +slug: thv_build +mdx: + format: md +--- + +## thv build + +Build a container for an MCP server without running it + +### Synopsis + +Build a container for an MCP server using a protocol scheme without running it. + +ToolHive supports building containers from protocol schemes: + + $ thv build uvx://package-name + $ thv build npx://package-name + $ thv build go://package-name + $ thv build go://./local-path + +Automatically generates a container that can run the specified package +using either uvx (Python with uv package manager), npx (Node.js), +or go (Golang). For Go, you can also specify local paths starting +with './' or '../' to build local Go projects. + +Build-time arguments can be baked into the container's ENTRYPOINT: + + $ thv build npx://@launchdarkly/mcp-server -- start + $ thv build uvx://package -- --transport stdio + +These arguments become part of the container image and will always run, +with runtime arguments (from 'thv run -- ') appending after them. + +The container will be built and tagged locally, ready to be used with 'thv run' +or other container tools. The built image name will be displayed upon successful completion. + +Examples: +$ thv build uvx://mcp-server-git +$ thv build --tag my-custom-name:latest npx://@modelcontextprotocol/server-filesystem +$ thv build go://./my-local-server +$ thv build npx://@launchdarkly/mcp-server -- start + +``` +thv build [flags] PROTOCOL [-- ARGS...] +``` + +### Options + +``` + --dry-run Generate Dockerfile without building (stdout output unless -o is set) (default false) + -h, --help help for build + -o, --output string Write the Dockerfile to the specified file instead of building (default builds an image instead of generating a Dockerfile) + -t, --tag string Name and optionally a tag in the 'name:tag' format for the built image (default generates a unique image name based on the package and transport type) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_client.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_client.md new file mode 100644 index 00000000..35c9879d --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_client.md @@ -0,0 +1,39 @@ +--- +title: thv client +hide_title: true +description: Reference for ToolHive CLI command `thv client` +last_update: + author: autogenerated +slug: thv_client +mdx: + format: md +--- + +## thv client + +Manage MCP clients + +### Synopsis + +The client command provides subcommands to manage MCP client integrations. + +### Options + +``` + -h, --help help for client +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers +- [thv client list-registered](thv_client_list-registered.md) - List all registered MCP clients +- [thv client register](thv_client_register.md) - Register a client for MCP server configuration +- [thv client remove](thv_client_remove.md) - Remove a client from MCP server configuration +- [thv client setup](thv_client_setup.md) - Interactively setup and register installed clients +- [thv client status](thv_client_status.md) - Show status of all supported MCP clients diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_client_list-registered.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_client_list-registered.md new file mode 100644 index 00000000..f104aaa3 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_client_list-registered.md @@ -0,0 +1,38 @@ +--- +title: thv client list-registered +hide_title: true +description: Reference for ToolHive CLI command `thv client list-registered` +last_update: + author: autogenerated +slug: thv_client_list-registered +mdx: + format: md +--- + +## thv client list-registered + +List all registered MCP clients + +### Synopsis + +List all clients that are registered for MCP server configuration. + +``` +thv client list-registered [flags] +``` + +### Options + +``` + -h, --help help for list-registered +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv client](thv_client.md) - Manage MCP clients diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_client_register.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_client_register.md new file mode 100644 index 00000000..1ac71f68 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_client_register.md @@ -0,0 +1,67 @@ +--- +title: thv client register +hide_title: true +description: Reference for ToolHive CLI command `thv client register` +last_update: + author: autogenerated +slug: thv_client_register +mdx: + format: md +--- + +## thv client register + +Register a client for MCP server configuration + +### Synopsis + +Register a client for MCP server configuration. + +Valid clients: + +- amp-cli: Sourcegraph Amp CLI +- amp-cursor: Cursor Sourcegraph Amp extension +- amp-vscode: VS Code Sourcegraph Amp extension +- amp-vscode-insider: VS Code Insiders Sourcegraph Amp extension +- amp-windsurf: Windsurf Sourcegraph Amp extension +- antigravity: Google Antigravity IDE +- claude-code: Claude Code CLI +- cline: VS Code Cline extension +- codex: OpenAI Codex CLI +- continue: Continue.dev IDE plugins +- cursor: Cursor editor +- gemini-cli: Google Gemini CLI +- goose: Goose AI agent +- kiro: Kiro AI IDE +- lm-studio: LM Studio application +- mistral-vibe: Mistral Vibe IDE +- opencode: OpenCode editor +- roo-code: VS Code Roo Code extension +- trae: Trae IDE +- vscode: Visual Studio Code +- vscode-insider: Visual Studio Code Insiders +- vscode-server: Microsoft's VS Code Server (remote development) +- windsurf: Windsurf IDE +- windsurf-jetbrains: Windsurf plugin for JetBrains IDEs +- zed: Zed editor + +``` +thv client register [client] [flags] +``` + +### Options + +``` + --group strings Only register workloads from specified groups (default [default]) + -h, --help help for register +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv client](thv_client.md) - Manage MCP clients diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_client_remove.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_client_remove.md new file mode 100644 index 00000000..89abd07d --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_client_remove.md @@ -0,0 +1,67 @@ +--- +title: thv client remove +hide_title: true +description: Reference for ToolHive CLI command `thv client remove` +last_update: + author: autogenerated +slug: thv_client_remove +mdx: + format: md +--- + +## thv client remove + +Remove a client from MCP server configuration + +### Synopsis + +Remove a client from MCP server configuration. + +Valid clients: + +- amp-cli: Sourcegraph Amp CLI +- amp-cursor: Cursor Sourcegraph Amp extension +- amp-vscode: VS Code Sourcegraph Amp extension +- amp-vscode-insider: VS Code Insiders Sourcegraph Amp extension +- amp-windsurf: Windsurf Sourcegraph Amp extension +- antigravity: Google Antigravity IDE +- claude-code: Claude Code CLI +- cline: VS Code Cline extension +- codex: OpenAI Codex CLI +- continue: Continue.dev IDE plugins +- cursor: Cursor editor +- gemini-cli: Google Gemini CLI +- goose: Goose AI agent +- kiro: Kiro AI IDE +- lm-studio: LM Studio application +- mistral-vibe: Mistral Vibe IDE +- opencode: OpenCode editor +- roo-code: VS Code Roo Code extension +- trae: Trae IDE +- vscode: Visual Studio Code +- vscode-insider: Visual Studio Code Insiders +- vscode-server: Microsoft's VS Code Server (remote development) +- windsurf: Windsurf IDE +- windsurf-jetbrains: Windsurf plugin for JetBrains IDEs +- zed: Zed editor + +``` +thv client remove [client] [flags] +``` + +### Options + +``` + --group strings Remove client from specified groups (if not set, removes all workloads from the client) + -h, --help help for remove +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv client](thv_client.md) - Manage MCP clients diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_client_setup.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_client_setup.md new file mode 100644 index 00000000..8050b90e --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_client_setup.md @@ -0,0 +1,38 @@ +--- +title: thv client setup +hide_title: true +description: Reference for ToolHive CLI command `thv client setup` +last_update: + author: autogenerated +slug: thv_client_setup +mdx: + format: md +--- + +## thv client setup + +Interactively setup and register installed clients + +### Synopsis + +Presents a list of installed but unregistered clients for interactive selection and registration. + +``` +thv client setup [flags] +``` + +### Options + +``` + -h, --help help for setup +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv client](thv_client.md) - Manage MCP clients diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_client_status.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_client_status.md new file mode 100644 index 00000000..b6566ea4 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_client_status.md @@ -0,0 +1,38 @@ +--- +title: thv client status +hide_title: true +description: Reference for ToolHive CLI command `thv client status` +last_update: + author: autogenerated +slug: thv_client_status +mdx: + format: md +--- + +## thv client status + +Show status of all supported MCP clients + +### Synopsis + +Display the installation and registration status of all supported MCP clients in a table format. + +``` +thv client status [flags] +``` + +### Options + +``` + -h, --help help for status +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv client](thv_client.md) - Manage MCP clients diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config.md new file mode 100644 index 00000000..b4fe500e --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config.md @@ -0,0 +1,48 @@ +--- +title: thv config +hide_title: true +description: Reference for ToolHive CLI command `thv config` +last_update: + author: autogenerated +slug: thv_config +mdx: + format: md +--- + +## thv config + +Manage application configuration + +### Synopsis + +The config command provides subcommands to manage application configuration settings. + +### Options + +``` + -h, --help help for config +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers +- [thv config get-build-auth-file](thv_config_get-build-auth-file.md) - Get build auth file configuration +- [thv config get-build-env](thv_config_get-build-env.md) - Get build environment variables +- [thv config get-ca-cert](thv_config_get-ca-cert.md) - Get the currently configured CA certificate path +- [thv config get-registry](thv_config_get-registry.md) - Get the currently configured registry +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration +- [thv config set-build-auth-file](thv_config_set-build-auth-file.md) - Set an auth file for protocol builds +- [thv config set-build-env](thv_config_set-build-env.md) - Set a build environment variable for protocol builds +- [thv config set-ca-cert](thv_config_set-ca-cert.md) - Set the default CA certificate for container builds +- [thv config set-registry](thv_config_set-registry.md) - Set the MCP server registry +- [thv config unset-build-auth-file](thv_config_unset-build-auth-file.md) - Remove build auth file(s) +- [thv config unset-build-env](thv_config_unset-build-env.md) - Remove build environment variable(s) +- [thv config unset-ca-cert](thv_config_unset-ca-cert.md) - Remove the configured CA certificate +- [thv config unset-registry](thv_config_unset-registry.md) - Remove the configured registry +- [thv config usage-metrics](thv_config_usage-metrics.md) - Enable or disable anonymous usage metrics diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_get-build-auth-file.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_get-build-auth-file.md new file mode 100644 index 00000000..f2db71f7 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_get-build-auth-file.md @@ -0,0 +1,49 @@ +--- +title: thv config get-build-auth-file +hide_title: true +description: Reference for ToolHive CLI command `thv config get-build-auth-file` +last_update: + author: autogenerated +slug: thv_config_get-build-auth-file +mdx: + format: md +--- + +## thv config get-build-auth-file + +Get build auth file configuration + +### Synopsis + +Display configured build auth files. +If a name is provided, shows only that specific file. +If no name is provided, shows all configured files. + +By default, file contents are hidden to prevent credential exposure. +Use --show-content to display the actual content. + +Examples: +thv config get-build-auth-file # Show all files (content hidden) +thv config get-build-auth-file npmrc # Show specific file (content hidden) +thv config get-build-auth-file npmrc --show-content # Show with content + +``` +thv config get-build-auth-file [name] [flags] +``` + +### Options + +``` + -h, --help help for get-build-auth-file + --show-content Show the actual file content (contains credentials) (default false) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_get-build-env.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_get-build-env.md new file mode 100644 index 00000000..9228fbbf --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_get-build-env.md @@ -0,0 +1,44 @@ +--- +title: thv config get-build-env +hide_title: true +description: Reference for ToolHive CLI command `thv config get-build-env` +last_update: + author: autogenerated +slug: thv_config_get-build-env +mdx: + format: md +--- + +## thv config get-build-env + +Get build environment variables + +### Synopsis + +Display configured build environment variables. +If a KEY is provided, shows only that specific variable. +If no KEY is provided, shows all configured variables. + +Examples: +thv config get-build-env # Show all variables +thv config get-build-env NPM_CONFIG_REGISTRY # Show specific variable + +``` +thv config get-build-env [KEY] [flags] +``` + +### Options + +``` + -h, --help help for get-build-env +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_get-ca-cert.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_get-ca-cert.md new file mode 100644 index 00000000..c724c654 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_get-ca-cert.md @@ -0,0 +1,38 @@ +--- +title: thv config get-ca-cert +hide_title: true +description: Reference for ToolHive CLI command `thv config get-ca-cert` +last_update: + author: autogenerated +slug: thv_config_get-ca-cert +mdx: + format: md +--- + +## thv config get-ca-cert + +Get the currently configured CA certificate path + +### Synopsis + +Display the path to the CA certificate file that is currently configured for container builds. + +``` +thv config get-ca-cert [flags] +``` + +### Options + +``` + -h, --help help for get-ca-cert +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_get-registry.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_get-registry.md new file mode 100644 index 00000000..494d79e5 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_get-registry.md @@ -0,0 +1,38 @@ +--- +title: thv config get-registry +hide_title: true +description: Reference for ToolHive CLI command `thv config get-registry` +last_update: + author: autogenerated +slug: thv_config_get-registry +mdx: + format: md +--- + +## thv config get-registry + +Get the currently configured registry + +### Synopsis + +Display the currently configured registry (URL or file path). + +``` +thv config get-registry [flags] +``` + +### Options + +``` + -h, --help help for get-registry +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel.md new file mode 100644 index 00000000..707c6d14 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel.md @@ -0,0 +1,55 @@ +--- +title: thv config otel +hide_title: true +description: Reference for ToolHive CLI command `thv config otel` +last_update: + author: autogenerated +slug: thv_config_otel +mdx: + format: md +--- + +## thv config otel + +Manage OpenTelemetry configuration + +### Synopsis + +Configure OpenTelemetry settings for observability and monitoring of MCP servers. + +### Options + +``` + -h, --help help for otel +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration +- [thv config otel get-enable-prometheus-metrics-path](thv_config_otel_get-enable-prometheus-metrics-path.md) - Get the currently configured OpenTelemetry Prometheus metrics path flag +- [thv config otel get-endpoint](thv_config_otel_get-endpoint.md) - Get the currently configured OpenTelemetry endpoint +- [thv config otel get-env-vars](thv_config_otel_get-env-vars.md) - Get the currently configured OpenTelemetry environment variables +- [thv config otel get-insecure](thv_config_otel_get-insecure.md) - Get the currently configured OpenTelemetry insecure transport flag +- [thv config otel get-metrics-enabled](thv_config_otel_get-metrics-enabled.md) - Get the currently configured OpenTelemetry metrics export flag +- [thv config otel get-sampling-rate](thv_config_otel_get-sampling-rate.md) - Get the currently configured OpenTelemetry sampling rate +- [thv config otel get-tracing-enabled](thv_config_otel_get-tracing-enabled.md) - Get the currently configured OpenTelemetry tracing export flag +- [thv config otel set-enable-prometheus-metrics-path](thv_config_otel_set-enable-prometheus-metrics-path.md) - Set the OpenTelemetry Prometheus metrics path flag +- [thv config otel set-endpoint](thv_config_otel_set-endpoint.md) - Set the OpenTelemetry endpoint URL +- [thv config otel set-env-vars](thv_config_otel_set-env-vars.md) - Set the OpenTelemetry environment variables +- [thv config otel set-insecure](thv_config_otel_set-insecure.md) - Set the OpenTelemetry insecure transport flag +- [thv config otel set-metrics-enabled](thv_config_otel_set-metrics-enabled.md) - Set the OpenTelemetry metrics export to enabled +- [thv config otel set-sampling-rate](thv_config_otel_set-sampling-rate.md) - Set the OpenTelemetry sampling rate +- [thv config otel set-tracing-enabled](thv_config_otel_set-tracing-enabled.md) - Set the OpenTelemetry tracing export to enabled +- [thv config otel unset-enable-prometheus-metrics-path](thv_config_otel_unset-enable-prometheus-metrics-path.md) - Remove the configured OpenTelemetry Prometheus metrics path flag +- [thv config otel unset-endpoint](thv_config_otel_unset-endpoint.md) - Remove the configured OpenTelemetry endpoint +- [thv config otel unset-env-vars](thv_config_otel_unset-env-vars.md) - Remove the configured OpenTelemetry environment variables +- [thv config otel unset-insecure](thv_config_otel_unset-insecure.md) - Remove the configured OpenTelemetry insecure transport flag +- [thv config otel unset-metrics-enabled](thv_config_otel_unset-metrics-enabled.md) - Remove the configured OpenTelemetry metrics export flag +- [thv config otel unset-sampling-rate](thv_config_otel_unset-sampling-rate.md) - Remove the configured OpenTelemetry sampling rate +- [thv config otel unset-tracing-enabled](thv_config_otel_unset-tracing-enabled.md) - Remove the configured OpenTelemetry tracing export flag diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_get-enable-prometheus-metrics-path.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_get-enable-prometheus-metrics-path.md new file mode 100644 index 00000000..39c763fc --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_get-enable-prometheus-metrics-path.md @@ -0,0 +1,38 @@ +--- +title: thv config otel get-enable-prometheus-metrics-path +hide_title: true +description: Reference for ToolHive CLI command `thv config otel get-enable-prometheus-metrics-path` +last_update: + author: autogenerated +slug: thv_config_otel_get-enable-prometheus-metrics-path +mdx: + format: md +--- + +## thv config otel get-enable-prometheus-metrics-path + +Get the currently configured OpenTelemetry Prometheus metrics path flag + +### Synopsis + +Display the OpenTelemetry Prometheus metrics path flag that is currently configured. + +``` +thv config otel get-enable-prometheus-metrics-path [flags] +``` + +### Options + +``` + -h, --help help for get-enable-prometheus-metrics-path +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_get-endpoint.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_get-endpoint.md new file mode 100644 index 00000000..9e5b40e0 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_get-endpoint.md @@ -0,0 +1,38 @@ +--- +title: thv config otel get-endpoint +hide_title: true +description: Reference for ToolHive CLI command `thv config otel get-endpoint` +last_update: + author: autogenerated +slug: thv_config_otel_get-endpoint +mdx: + format: md +--- + +## thv config otel get-endpoint + +Get the currently configured OpenTelemetry endpoint + +### Synopsis + +Display the OpenTelemetry endpoint URL that is currently configured. + +``` +thv config otel get-endpoint [flags] +``` + +### Options + +``` + -h, --help help for get-endpoint +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_get-env-vars.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_get-env-vars.md new file mode 100644 index 00000000..1763e558 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_get-env-vars.md @@ -0,0 +1,38 @@ +--- +title: thv config otel get-env-vars +hide_title: true +description: Reference for ToolHive CLI command `thv config otel get-env-vars` +last_update: + author: autogenerated +slug: thv_config_otel_get-env-vars +mdx: + format: md +--- + +## thv config otel get-env-vars + +Get the currently configured OpenTelemetry environment variables + +### Synopsis + +Display the OpenTelemetry environment variables that are currently configured. + +``` +thv config otel get-env-vars [flags] +``` + +### Options + +``` + -h, --help help for get-env-vars +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_get-insecure.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_get-insecure.md new file mode 100644 index 00000000..2445c685 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_get-insecure.md @@ -0,0 +1,38 @@ +--- +title: thv config otel get-insecure +hide_title: true +description: Reference for ToolHive CLI command `thv config otel get-insecure` +last_update: + author: autogenerated +slug: thv_config_otel_get-insecure +mdx: + format: md +--- + +## thv config otel get-insecure + +Get the currently configured OpenTelemetry insecure transport flag + +### Synopsis + +Display the OpenTelemetry insecure transport flag that is currently configured. + +``` +thv config otel get-insecure [flags] +``` + +### Options + +``` + -h, --help help for get-insecure +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_get-metrics-enabled.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_get-metrics-enabled.md new file mode 100644 index 00000000..a0f9bdb7 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_get-metrics-enabled.md @@ -0,0 +1,38 @@ +--- +title: thv config otel get-metrics-enabled +hide_title: true +description: Reference for ToolHive CLI command `thv config otel get-metrics-enabled` +last_update: + author: autogenerated +slug: thv_config_otel_get-metrics-enabled +mdx: + format: md +--- + +## thv config otel get-metrics-enabled + +Get the currently configured OpenTelemetry metrics export flag + +### Synopsis + +Display the OpenTelemetry metrics export flag that is currently configured. + +``` +thv config otel get-metrics-enabled [flags] +``` + +### Options + +``` + -h, --help help for get-metrics-enabled +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_get-sampling-rate.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_get-sampling-rate.md new file mode 100644 index 00000000..c83bcb85 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_get-sampling-rate.md @@ -0,0 +1,38 @@ +--- +title: thv config otel get-sampling-rate +hide_title: true +description: Reference for ToolHive CLI command `thv config otel get-sampling-rate` +last_update: + author: autogenerated +slug: thv_config_otel_get-sampling-rate +mdx: + format: md +--- + +## thv config otel get-sampling-rate + +Get the currently configured OpenTelemetry sampling rate + +### Synopsis + +Display the OpenTelemetry sampling rate that is currently configured. + +``` +thv config otel get-sampling-rate [flags] +``` + +### Options + +``` + -h, --help help for get-sampling-rate +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_get-tracing-enabled.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_get-tracing-enabled.md new file mode 100644 index 00000000..7b267175 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_get-tracing-enabled.md @@ -0,0 +1,38 @@ +--- +title: thv config otel get-tracing-enabled +hide_title: true +description: Reference for ToolHive CLI command `thv config otel get-tracing-enabled` +last_update: + author: autogenerated +slug: thv_config_otel_get-tracing-enabled +mdx: + format: md +--- + +## thv config otel get-tracing-enabled + +Get the currently configured OpenTelemetry tracing export flag + +### Synopsis + +Display the OpenTelemetry tracing export flag that is currently configured. + +``` +thv config otel get-tracing-enabled [flags] +``` + +### Options + +``` + -h, --help help for get-tracing-enabled +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_set-enable-prometheus-metrics-path.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_set-enable-prometheus-metrics-path.md new file mode 100644 index 00000000..ac86ed05 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_set-enable-prometheus-metrics-path.md @@ -0,0 +1,40 @@ +--- +title: thv config otel set-enable-prometheus-metrics-path +hide_title: true +description: Reference for ToolHive CLI command `thv config otel set-enable-prometheus-metrics-path` +last_update: + author: autogenerated +slug: thv_config_otel_set-enable-prometheus-metrics-path +mdx: + format: md +--- + +## thv config otel set-enable-prometheus-metrics-path + +Set the OpenTelemetry Prometheus metrics path flag + +### Synopsis + +Set the OpenTelemetry Prometheus metrics path flag to enable /metrics endpoint. + + thv config otel set-enable-prometheus-metrics-path true + +``` +thv config otel set-enable-prometheus-metrics-path [flags] +``` + +### Options + +``` + -h, --help help for set-enable-prometheus-metrics-path +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_set-endpoint.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_set-endpoint.md new file mode 100644 index 00000000..b98b2fbf --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_set-endpoint.md @@ -0,0 +1,44 @@ +--- +title: thv config otel set-endpoint +hide_title: true +description: Reference for ToolHive CLI command `thv config otel set-endpoint` +last_update: + author: autogenerated +slug: thv_config_otel_set-endpoint +mdx: + format: md +--- + +## thv config otel set-endpoint + +Set the OpenTelemetry endpoint URL + +### Synopsis + +Set the OpenTelemetry OTLP endpoint URL for tracing and metrics. + +This endpoint will be used by default when running MCP servers unless overridden by the --otel-endpoint flag. + +Example: + + thv config otel set-endpoint https://api.honeycomb.io + +``` +thv config otel set-endpoint [flags] +``` + +### Options + +``` + -h, --help help for set-endpoint +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_set-env-vars.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_set-env-vars.md new file mode 100644 index 00000000..7ec87b4d --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_set-env-vars.md @@ -0,0 +1,44 @@ +--- +title: thv config otel set-env-vars +hide_title: true +description: Reference for ToolHive CLI command `thv config otel set-env-vars` +last_update: + author: autogenerated +slug: thv_config_otel_set-env-vars +mdx: + format: md +--- + +## thv config otel set-env-vars + +Set the OpenTelemetry environment variables + +### Synopsis + +Set the list of environment variable names to include in OpenTelemetry spans. + +These environment variables will be used by default when running MCP servers unless overridden by the --otel-env-vars flag. + +Example: + + thv config otel set-env-vars USER,HOME,PATH + +``` +thv config otel set-env-vars [flags] +``` + +### Options + +``` + -h, --help help for set-env-vars +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_set-insecure.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_set-insecure.md new file mode 100644 index 00000000..da645ac1 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_set-insecure.md @@ -0,0 +1,40 @@ +--- +title: thv config otel set-insecure +hide_title: true +description: Reference for ToolHive CLI command `thv config otel set-insecure` +last_update: + author: autogenerated +slug: thv_config_otel_set-insecure +mdx: + format: md +--- + +## thv config otel set-insecure + +Set the OpenTelemetry insecure transport flag + +### Synopsis + +Set the OpenTelemetry insecure flag to enable HTTP instead of HTTPS for OTLP endpoints. + + thv config otel set-insecure true + +``` +thv config otel set-insecure [flags] +``` + +### Options + +``` + -h, --help help for set-insecure +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_set-metrics-enabled.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_set-metrics-enabled.md new file mode 100644 index 00000000..30ffddea --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_set-metrics-enabled.md @@ -0,0 +1,40 @@ +--- +title: thv config otel set-metrics-enabled +hide_title: true +description: Reference for ToolHive CLI command `thv config otel set-metrics-enabled` +last_update: + author: autogenerated +slug: thv_config_otel_set-metrics-enabled +mdx: + format: md +--- + +## thv config otel set-metrics-enabled + +Set the OpenTelemetry metrics export to enabled + +### Synopsis + +Set the OpenTelemetry metrics flag to enable to export metrics to an OTel collector. + + thv config otel set-metrics-enabled true + +``` +thv config otel set-metrics-enabled [flags] +``` + +### Options + +``` + -h, --help help for set-metrics-enabled +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_set-sampling-rate.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_set-sampling-rate.md new file mode 100644 index 00000000..212d42a0 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_set-sampling-rate.md @@ -0,0 +1,44 @@ +--- +title: thv config otel set-sampling-rate +hide_title: true +description: Reference for ToolHive CLI command `thv config otel set-sampling-rate` +last_update: + author: autogenerated +slug: thv_config_otel_set-sampling-rate +mdx: + format: md +--- + +## thv config otel set-sampling-rate + +Set the OpenTelemetry sampling rate + +### Synopsis + +Set the OpenTelemetry trace sampling rate (between 0.0 and 1.0). + +This sampling rate will be used by default when running MCP servers unless overridden by the --otel-sampling-rate flag. + +Example: + + thv config otel set-sampling-rate 0.1 + +``` +thv config otel set-sampling-rate [flags] +``` + +### Options + +``` + -h, --help help for set-sampling-rate +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_set-tracing-enabled.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_set-tracing-enabled.md new file mode 100644 index 00000000..03692e58 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_set-tracing-enabled.md @@ -0,0 +1,40 @@ +--- +title: thv config otel set-tracing-enabled +hide_title: true +description: Reference for ToolHive CLI command `thv config otel set-tracing-enabled` +last_update: + author: autogenerated +slug: thv_config_otel_set-tracing-enabled +mdx: + format: md +--- + +## thv config otel set-tracing-enabled + +Set the OpenTelemetry tracing export to enabled + +### Synopsis + +Set the OpenTelemetry tracing flag to enable to export traces to an OTel collector. + + thv config otel set-tracing-enabled true + +``` +thv config otel set-tracing-enabled [flags] +``` + +### Options + +``` + -h, --help help for set-tracing-enabled +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_unset-enable-prometheus-metrics-path.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_unset-enable-prometheus-metrics-path.md new file mode 100644 index 00000000..4abb8663 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_unset-enable-prometheus-metrics-path.md @@ -0,0 +1,38 @@ +--- +title: thv config otel unset-enable-prometheus-metrics-path +hide_title: true +description: Reference for ToolHive CLI command `thv config otel unset-enable-prometheus-metrics-path` +last_update: + author: autogenerated +slug: thv_config_otel_unset-enable-prometheus-metrics-path +mdx: + format: md +--- + +## thv config otel unset-enable-prometheus-metrics-path + +Remove the configured OpenTelemetry Prometheus metrics path flag + +### Synopsis + +Remove the OpenTelemetry Prometheus metrics path flag configuration. + +``` +thv config otel unset-enable-prometheus-metrics-path [flags] +``` + +### Options + +``` + -h, --help help for unset-enable-prometheus-metrics-path +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_unset-endpoint.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_unset-endpoint.md new file mode 100644 index 00000000..7d58e48f --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_unset-endpoint.md @@ -0,0 +1,38 @@ +--- +title: thv config otel unset-endpoint +hide_title: true +description: Reference for ToolHive CLI command `thv config otel unset-endpoint` +last_update: + author: autogenerated +slug: thv_config_otel_unset-endpoint +mdx: + format: md +--- + +## thv config otel unset-endpoint + +Remove the configured OpenTelemetry endpoint + +### Synopsis + +Remove the OpenTelemetry endpoint configuration. + +``` +thv config otel unset-endpoint [flags] +``` + +### Options + +``` + -h, --help help for unset-endpoint +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_unset-env-vars.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_unset-env-vars.md new file mode 100644 index 00000000..20839e51 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_unset-env-vars.md @@ -0,0 +1,38 @@ +--- +title: thv config otel unset-env-vars +hide_title: true +description: Reference for ToolHive CLI command `thv config otel unset-env-vars` +last_update: + author: autogenerated +slug: thv_config_otel_unset-env-vars +mdx: + format: md +--- + +## thv config otel unset-env-vars + +Remove the configured OpenTelemetry environment variables + +### Synopsis + +Remove the OpenTelemetry environment variables configuration. + +``` +thv config otel unset-env-vars [flags] +``` + +### Options + +``` + -h, --help help for unset-env-vars +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_unset-insecure.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_unset-insecure.md new file mode 100644 index 00000000..15b4f4e5 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_unset-insecure.md @@ -0,0 +1,38 @@ +--- +title: thv config otel unset-insecure +hide_title: true +description: Reference for ToolHive CLI command `thv config otel unset-insecure` +last_update: + author: autogenerated +slug: thv_config_otel_unset-insecure +mdx: + format: md +--- + +## thv config otel unset-insecure + +Remove the configured OpenTelemetry insecure transport flag + +### Synopsis + +Remove the OpenTelemetry insecure transport flag configuration. + +``` +thv config otel unset-insecure [flags] +``` + +### Options + +``` + -h, --help help for unset-insecure +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_unset-metrics-enabled.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_unset-metrics-enabled.md new file mode 100644 index 00000000..af9271e5 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_unset-metrics-enabled.md @@ -0,0 +1,38 @@ +--- +title: thv config otel unset-metrics-enabled +hide_title: true +description: Reference for ToolHive CLI command `thv config otel unset-metrics-enabled` +last_update: + author: autogenerated +slug: thv_config_otel_unset-metrics-enabled +mdx: + format: md +--- + +## thv config otel unset-metrics-enabled + +Remove the configured OpenTelemetry metrics export flag + +### Synopsis + +Remove the OpenTelemetry metrics export flag configuration. + +``` +thv config otel unset-metrics-enabled [flags] +``` + +### Options + +``` + -h, --help help for unset-metrics-enabled +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_unset-sampling-rate.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_unset-sampling-rate.md new file mode 100644 index 00000000..199b9eb0 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_unset-sampling-rate.md @@ -0,0 +1,38 @@ +--- +title: thv config otel unset-sampling-rate +hide_title: true +description: Reference for ToolHive CLI command `thv config otel unset-sampling-rate` +last_update: + author: autogenerated +slug: thv_config_otel_unset-sampling-rate +mdx: + format: md +--- + +## thv config otel unset-sampling-rate + +Remove the configured OpenTelemetry sampling rate + +### Synopsis + +Remove the OpenTelemetry sampling rate configuration. + +``` +thv config otel unset-sampling-rate [flags] +``` + +### Options + +``` + -h, --help help for unset-sampling-rate +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_unset-tracing-enabled.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_unset-tracing-enabled.md new file mode 100644 index 00000000..dbbcfc00 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_otel_unset-tracing-enabled.md @@ -0,0 +1,38 @@ +--- +title: thv config otel unset-tracing-enabled +hide_title: true +description: Reference for ToolHive CLI command `thv config otel unset-tracing-enabled` +last_update: + author: autogenerated +slug: thv_config_otel_unset-tracing-enabled +mdx: + format: md +--- + +## thv config otel unset-tracing-enabled + +Remove the configured OpenTelemetry tracing export flag + +### Synopsis + +Remove the OpenTelemetry tracing export flag configuration. + +``` +thv config otel unset-tracing-enabled [flags] +``` + +### Options + +``` + -h, --help help for unset-tracing-enabled +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config otel](thv_config_otel.md) - Manage OpenTelemetry configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_set-build-auth-file.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_set-build-auth-file.md new file mode 100644 index 00000000..04751d2d --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_set-build-auth-file.md @@ -0,0 +1,66 @@ +--- +title: thv config set-build-auth-file +hide_title: true +description: Reference for ToolHive CLI command `thv config set-build-auth-file` +last_update: + author: autogenerated +slug: thv_config_set-build-auth-file +mdx: + format: md +--- + +## thv config set-build-auth-file + +Set an auth file for protocol builds + +### Synopsis + +Set authentication file content that will be injected into the container +during protocol builds (npx://, uvx://, go://). This is useful for authenticating +to private package registries. + +Supported file types: +npmrc - NPM configuration (~/.npmrc) for npm/npx registries +netrc - Netrc file (~/.netrc) for pip, Go, and other tools +yarnrc - Yarn configuration (~/.yarnrc) + +The file content is injected into the build stage only and is NOT included +in the final container image. + +Examples: + +# Set npmrc for private npm registry + +thv config set-build-auth-file npmrc '//npm.corp.example.com/:\_authToken=TOKEN' + +# Set netrc for pip/Go authentication + +thv config set-build-auth-file netrc 'machine github.com login git password TOKEN' + +# Read content from stdin (avoids exposing secrets in shell history) + +cat ~/.npmrc | thv config set-build-auth-file npmrc --stdin +thv config set-build-auth-file npmrc --stdin < ~/.npmrc + +Note: For multi-line content, use quotes, heredoc syntax, or --stdin. + +``` +thv config set-build-auth-file [content] [flags] +``` + +### Options + +``` + -h, --help help for set-build-auth-file + --stdin Read file content from stdin instead of command line argument (default false) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_set-build-env.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_set-build-env.md new file mode 100644 index 00000000..485102e7 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_set-build-env.md @@ -0,0 +1,67 @@ +--- +title: thv config set-build-env +hide_title: true +description: Reference for ToolHive CLI command `thv config set-build-env` +last_update: + author: autogenerated +slug: thv_config_set-build-env +mdx: + format: md +--- + +## thv config set-build-env + +Set a build environment variable for protocol builds + +### Synopsis + +Set a build environment variable that will be injected into Dockerfiles +during protocol builds (npx://, uvx://, go://). This is useful for configuring +custom package mirrors in corporate environments. + +Environment variable names must: + +- Start with an uppercase letter +- Contain only uppercase letters, numbers, and underscores +- Not be a reserved system variable (PATH, HOME, etc.) + +You can set the value in three ways: + +1. Directly: thv config set-build-env KEY value +2. From a ToolHive secret: thv config set-build-env KEY --from-secret secret-name +3. From shell environment: thv config set-build-env KEY --from-env + +Common use cases: + +- NPM_CONFIG_REGISTRY: Custom npm registry URL +- PIP_INDEX_URL: Custom PyPI index URL +- UV_DEFAULT_INDEX: Custom uv package index URL +- GOPROXY: Custom Go module proxy URL +- GOPRIVATE: Private Go module paths + +Examples: +thv config set-build-env NPM_CONFIG_REGISTRY https://npm.corp.example.com +thv config set-build-env GITHUB_TOKEN --from-secret github-pat +thv config set-build-env ARTIFACTORY_API_KEY --from-env + +``` +thv config set-build-env [value] [flags] +``` + +### Options + +``` + --from-env Read value from shell environment at build time + --from-secret Read value from a ToolHive secret at build time (value argument becomes secret name) + -h, --help help for set-build-env +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_set-ca-cert.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_set-ca-cert.md new file mode 100644 index 00000000..48c9fbda --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_set-ca-cert.md @@ -0,0 +1,42 @@ +--- +title: thv config set-ca-cert +hide_title: true +description: Reference for ToolHive CLI command `thv config set-ca-cert` +last_update: + author: autogenerated +slug: thv_config_set-ca-cert +mdx: + format: md +--- + +## thv config set-ca-cert + +Set the default CA certificate for container builds + +### Synopsis + +Set the default CA certificate file path that will be used for all container builds. +This is useful in corporate environments with TLS inspection where custom CA certificates are required. + +Example: +thv config set-ca-cert /path/to/corporate-ca.crt + +``` +thv config set-ca-cert [flags] +``` + +### Options + +``` + -h, --help help for set-ca-cert +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_set-registry.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_set-registry.md new file mode 100644 index 00000000..e2a25d69 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_set-registry.md @@ -0,0 +1,59 @@ +--- +title: thv config set-registry +hide_title: true +description: Reference for ToolHive CLI command `thv config set-registry` +last_update: + author: autogenerated +slug: thv_config_set-registry +mdx: + format: md +--- + +## thv config set-registry + +Set the MCP server registry + +### Synopsis + +Set the MCP server registry to a remote URL, local file path, or API endpoint. +The command automatically detects the registry type: + +- URLs ending with .json are treated as static registry files +- Other URLs are treated as MCP Registry API endpoints (v0.1 spec) +- Local paths are treated as local registry files + +Any previously configured registry authentication is cleared when this command is run. +To configure OIDC authentication, provide --issuer and --client-id flags. + +Examples: +thv config set-registry https://example.com/registry.json # Static remote file +thv config set-registry https://registry.example.com # API endpoint +thv config set-registry /path/to/local-registry.json # Local file path +thv config set-registry file:///path/to/local-registry.json # Explicit file URL +thv config set-registry https://registry.example.com \ + --issuer https://auth.company.com --client-id toolhive-cli # With OAuth auth + +``` +thv config set-registry [flags] +``` + +### Options + +``` + -p, --allow-private-ip Allow setting the registry URL or API endpoint, even if it references a private IP address (default false) + --audience string OAuth audience parameter for registry authentication + --client-id string OAuth client ID for registry authentication + -h, --help help for set-registry + --issuer string OIDC issuer URL for registry authentication + --scopes strings OAuth scopes for registry authentication (default [openid,offline_access]) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_unset-build-auth-file.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_unset-build-auth-file.md new file mode 100644 index 00000000..7e3264d8 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_unset-build-auth-file.md @@ -0,0 +1,43 @@ +--- +title: thv config unset-build-auth-file +hide_title: true +description: Reference for ToolHive CLI command `thv config unset-build-auth-file` +last_update: + author: autogenerated +slug: thv_config_unset-build-auth-file +mdx: + format: md +--- + +## thv config unset-build-auth-file + +Remove build auth file(s) + +### Synopsis + +Remove a specific build auth file or all files. + +Examples: +thv config unset-build-auth-file npmrc # Remove specific file +thv config unset-build-auth-file --all # Remove all files + +``` +thv config unset-build-auth-file [name] [flags] +``` + +### Options + +``` + --all Remove all build auth files + -h, --help help for unset-build-auth-file +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_unset-build-env.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_unset-build-env.md new file mode 100644 index 00000000..a6addeaa --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_unset-build-env.md @@ -0,0 +1,43 @@ +--- +title: thv config unset-build-env +hide_title: true +description: Reference for ToolHive CLI command `thv config unset-build-env` +last_update: + author: autogenerated +slug: thv_config_unset-build-env +mdx: + format: md +--- + +## thv config unset-build-env + +Remove build environment variable(s) + +### Synopsis + +Remove a specific build environment variable or all variables. + +Examples: +thv config unset-build-env NPM_CONFIG_REGISTRY # Remove specific variable +thv config unset-build-env --all # Remove all variables + +``` +thv config unset-build-env [KEY] [flags] +``` + +### Options + +``` + --all Remove all build environment variables + -h, --help help for unset-build-env +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_unset-ca-cert.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_unset-ca-cert.md new file mode 100644 index 00000000..b6fb8992 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_unset-ca-cert.md @@ -0,0 +1,38 @@ +--- +title: thv config unset-ca-cert +hide_title: true +description: Reference for ToolHive CLI command `thv config unset-ca-cert` +last_update: + author: autogenerated +slug: thv_config_unset-ca-cert +mdx: + format: md +--- + +## thv config unset-ca-cert + +Remove the configured CA certificate + +### Synopsis + +Remove the CA certificate configuration, reverting to default behavior without custom CA certificates. + +``` +thv config unset-ca-cert [flags] +``` + +### Options + +``` + -h, --help help for unset-ca-cert +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_unset-registry.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_unset-registry.md new file mode 100644 index 00000000..00688b5a --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_unset-registry.md @@ -0,0 +1,38 @@ +--- +title: thv config unset-registry +hide_title: true +description: Reference for ToolHive CLI command `thv config unset-registry` +last_update: + author: autogenerated +slug: thv_config_unset-registry +mdx: + format: md +--- + +## thv config unset-registry + +Remove the configured registry + +### Synopsis + +Remove the registry configuration, reverting to the built-in registry. + +``` +thv config unset-registry [flags] +``` + +### Options + +``` + -h, --help help for unset-registry +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_usage-metrics.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_usage-metrics.md new file mode 100644 index 00000000..5ed0914f --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_config_usage-metrics.md @@ -0,0 +1,34 @@ +--- +title: thv config usage-metrics +hide_title: true +description: Reference for ToolHive CLI command `thv config usage-metrics` +last_update: + author: autogenerated +slug: thv_config_usage-metrics +mdx: + format: md +--- + +## thv config usage-metrics + +Enable or disable anonymous usage metrics + +``` +thv config usage-metrics [flags] +``` + +### Options + +``` + -h, --help help for usage-metrics +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv config](thv_config.md) - Manage application configuration diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_export.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_export.md new file mode 100644 index 00000000..74638285 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_export.md @@ -0,0 +1,58 @@ +--- +title: thv export +hide_title: true +description: Reference for ToolHive CLI command `thv export` +last_update: + author: autogenerated +slug: thv_export +mdx: + format: md +--- + +## thv export + +Export a workload's run configuration to a file + +### Synopsis + +Export a workload's run configuration to a file for sharing or backup. + +The exported configuration can be used with 'thv run --from-config ' to recreate +the same workload with identical settings. + +You can export in different formats: + +- json: Export as RunConfig JSON (default, can be used with 'thv run --from-config') +- k8s: Export as Kubernetes MCPServer resource YAML + +Examples: + + # Export a workload configuration to a JSON file + thv export my-server ./my-server-config.json + + # Export as Kubernetes MCPServer resource + thv export my-server ./my-server.yaml --format k8s + + # Export to a specific directory + thv export github-mcp /tmp/configs/github-config.json + +``` +thv export [flags] +``` + +### Options + +``` + --format string Export format: json or k8s (default "json") + -h, --help help for export +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_group.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_group.md new file mode 100644 index 00000000..0ec6fedb --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_group.md @@ -0,0 +1,38 @@ +--- +title: thv group +hide_title: true +description: Reference for ToolHive CLI command `thv group` +last_update: + author: autogenerated +slug: thv_group +mdx: + format: md +--- + +## thv group + +Manage logical groupings of MCP servers + +### Synopsis + +The group command provides subcommands to manage logical groupings of MCP servers. + +### Options + +``` + -h, --help help for group +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers +- [thv group create](thv_group_create.md) - Create a new group of MCP servers +- [thv group list](thv_group_list.md) - List all groups +- [thv group rm](thv_group_rm.md) - Remove a group and remove workloads from it +- [thv group run](thv_group_run.md) - Deploy all MCP servers from a registry group diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_group_create.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_group_create.md new file mode 100644 index 00000000..72b6a873 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_group_create.md @@ -0,0 +1,39 @@ +--- +title: thv group create +hide_title: true +description: Reference for ToolHive CLI command `thv group create` +last_update: + author: autogenerated +slug: thv_group_create +mdx: + format: md +--- + +## thv group create + +Create a new group of MCP servers + +### Synopsis + +Create a new logical group of MCP servers. +The group can be used to organize and manage multiple MCP servers together. + +``` +thv group create [group-name] [flags] +``` + +### Options + +``` + -h, --help help for create +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv group](thv_group.md) - Manage logical groupings of MCP servers diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_group_list.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_group_list.md new file mode 100644 index 00000000..4e7dbb66 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_group_list.md @@ -0,0 +1,38 @@ +--- +title: thv group list +hide_title: true +description: Reference for ToolHive CLI command `thv group list` +last_update: + author: autogenerated +slug: thv_group_list +mdx: + format: md +--- + +## thv group list + +List all groups + +### Synopsis + +List all logical groups of MCP servers. + +``` +thv group list [flags] +``` + +### Options + +``` + -h, --help help for list +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv group](thv_group.md) - Manage logical groupings of MCP servers diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_group_rm.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_group_rm.md new file mode 100644 index 00000000..6405bc2a --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_group_rm.md @@ -0,0 +1,39 @@ +--- +title: thv group rm +hide_title: true +description: Reference for ToolHive CLI command `thv group rm` +last_update: + author: autogenerated +slug: thv_group_rm +mdx: + format: md +--- + +## thv group rm + +Remove a group and remove workloads from it + +### Synopsis + +Remove a group and remove all MCP servers from it. By default, this only removes the group membership from workloads without deleting them. Use --with-workloads to also delete the workloads. + +``` +thv group rm [group-name] [flags] +``` + +### Options + +``` + -h, --help help for rm + --with-workloads Delete all workloads in the group along with the group (default false) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv group](thv_group.md) - Manage logical groupings of MCP servers diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_group_run.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_group_run.md new file mode 100644 index 00000000..264e225e --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_group_run.md @@ -0,0 +1,41 @@ +--- +title: thv group run +hide_title: true +description: Reference for ToolHive CLI command `thv group run` +last_update: + author: autogenerated +slug: thv_group_run +mdx: + format: md +--- + +## thv group run + +Deploy all MCP servers from a registry group + +### Synopsis + +Deploy all MCP servers defined in a registry group. +This creates a new runtime group and starts all MCP servers within it. + +``` +thv group run [group-name] [flags] +``` + +### Options + +``` + --env stringArray Environment variables to pass to an MCP server in the group (format: SERVER_NAME.KEY=VALUE) + -h, --help help for run + --secret stringArray Secrets to be fetched from the secrets manager and set as environment variables (format: NAME,target=SERVER_NAME.TARGET) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv group](thv_group.md) - Manage logical groupings of MCP servers diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_inspector.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_inspector.md new file mode 100644 index 00000000..cf16cf0e --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_inspector.md @@ -0,0 +1,40 @@ +--- +title: thv inspector +hide_title: true +description: Reference for ToolHive CLI command `thv inspector` +last_update: + author: autogenerated +slug: thv_inspector +mdx: + format: md +--- + +## thv inspector + +Launches the MCP Inspector UI and connects it to the specified MCP server + +### Synopsis + +Launches the MCP Inspector UI and connects it to the specified MCP server + +``` +thv inspector [workload-name] [flags] +``` + +### Options + +``` + -h, --help help for inspector + -p, --mcp-proxy-port int Port to run the MCP Proxy on (default 6277) + -u, --ui-port int Port to run the MCP Inspector UI on (default 6274) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_list.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_list.md new file mode 100644 index 00000000..abe24057 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_list.md @@ -0,0 +1,64 @@ +--- +title: thv list +hide_title: true +description: Reference for ToolHive CLI command `thv list` +last_update: + author: autogenerated +slug: thv_list +mdx: + format: md +--- + +## thv list + +List running MCP servers + +### Synopsis + +List all MCP servers managed by ToolHive, including their status and configuration. + +Examples: + +# List running MCP servers + +thv list + +# List all MCP servers (including stopped) + +thv list --all + +# List servers in JSON format + +thv list --format json + +# List servers in a specific group + +thv list --group production + +# List servers with specific labels + +thv list --label env=dev --label team=backend + +``` +thv list [flags] +``` + +### Options + +``` + -a, --all Show all workloads (default shows just running) + --format string Output format (json, text, mcpservers) (default "text") + --group string Filter by group + -h, --help help for list + -l, --label stringArray Filter workloads by labels (format: key=value) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_logs.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_logs.md new file mode 100644 index 00000000..63d563a1 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_logs.md @@ -0,0 +1,62 @@ +--- +title: thv logs +hide_title: true +description: Reference for ToolHive CLI command `thv logs` +last_update: + author: autogenerated +slug: thv_logs +mdx: + format: md +--- + +## thv logs + +Output the logs of an MCP server or manage log files + +### Synopsis + +Output the logs of an MCP server managed by ToolHive, or manage log files. + +By default, this command shows the logs from the MCP server container. +Use --proxy to view the logs from the ToolHive proxy process instead. + +Examples: + +# View logs of an MCP server + +thv logs filesystem + +# Follow logs in real-time + +thv logs filesystem --follow + +# View proxy logs instead of container logs + +thv logs filesystem --proxy + +# Clean up old log files + +thv logs prune + +``` +thv logs [workload-name|prune] [flags] +``` + +### Options + +``` + -f, --follow Follow log output (only for workload logs) (default false) + -h, --help help for logs + -p, --proxy Show proxy logs instead of container logs (default false) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers +- [thv logs prune](thv_logs_prune.md) - Delete log files from servers not currently managed by ToolHive diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_logs_prune.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_logs_prune.md new file mode 100644 index 00000000..bd7ca809 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_logs_prune.md @@ -0,0 +1,39 @@ +--- +title: thv logs prune +hide_title: true +description: Reference for ToolHive CLI command `thv logs prune` +last_update: + author: autogenerated +slug: thv_logs_prune +mdx: + format: md +--- + +## thv logs prune + +Delete log files from servers not currently managed by ToolHive + +### Synopsis + +Delete log files from servers that are not currently managed by ToolHive (running or stopped). +This helps clean up old log files that accumulate over time from removed servers. + +``` +thv logs prune [flags] +``` + +### Options + +``` + -h, --help help for prune +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv logs](thv_logs.md) - Output the logs of an MCP server or manage log files diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_mcp.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_mcp.md new file mode 100644 index 00000000..4a3d91f0 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_mcp.md @@ -0,0 +1,36 @@ +--- +title: thv mcp +hide_title: true +description: Reference for ToolHive CLI command `thv mcp` +last_update: + author: autogenerated +slug: thv_mcp +mdx: + format: md +--- + +## thv mcp + +Interact with MCP servers for debugging + +### Synopsis + +The mcp command provides subcommands to interact with MCP (Model Context Protocol) servers for debugging purposes. + +### Options + +``` + -h, --help help for mcp +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers +- [thv mcp list](thv_mcp_list.md) - List MCP server capabilities +- [thv mcp serve](thv_mcp_serve.md) - 🧪 EXPERIMENTAL: Start an MCP server to control ToolHive diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_mcp_list.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_mcp_list.md new file mode 100644 index 00000000..51139e47 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_mcp_list.md @@ -0,0 +1,45 @@ +--- +title: thv mcp list +hide_title: true +description: Reference for ToolHive CLI command `thv mcp list` +last_update: + author: autogenerated +slug: thv_mcp_list +mdx: + format: md +--- + +## thv mcp list + +List MCP server capabilities + +### Synopsis + +List tools, resources, and prompts available from an MCP server. Use subcommands to list specific types. + +``` +thv mcp list [tools|resources|prompts] [flags] +``` + +### Options + +``` + --format string Output format (json, text) (default "text") + -h, --help help for list + --server string MCP server URL or name from ToolHive registry (required) + --timeout duration Connection timeout (default 30s) + --transport string Transport type (auto, sse, streamable-http) (default "auto") +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv mcp](thv_mcp.md) - Interact with MCP servers for debugging +- [thv mcp list prompts](thv_mcp_list_prompts.md) - List available prompts from MCP server +- [thv mcp list resources](thv_mcp_list_resources.md) - List available resources from MCP server +- [thv mcp list tools](thv_mcp_list_tools.md) - List available tools from MCP server diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_mcp_list_prompts.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_mcp_list_prompts.md new file mode 100644 index 00000000..b6a2a844 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_mcp_list_prompts.md @@ -0,0 +1,42 @@ +--- +title: thv mcp list prompts +hide_title: true +description: Reference for ToolHive CLI command `thv mcp list prompts` +last_update: + author: autogenerated +slug: thv_mcp_list_prompts +mdx: + format: md +--- + +## thv mcp list prompts + +List available prompts from MCP server + +### Synopsis + +List all prompts available from the specified MCP server. + +``` +thv mcp list prompts [flags] +``` + +### Options + +``` + --format string Output format (json, text) (default "text") + -h, --help help for prompts + --server string MCP server URL or name from ToolHive registry (required) + --timeout duration Connection timeout (default 30s) + --transport string Transport type (auto, sse, streamable-http) (default "auto") +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv mcp list](thv_mcp_list.md) - List MCP server capabilities diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_mcp_list_resources.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_mcp_list_resources.md new file mode 100644 index 00000000..1ee5408e --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_mcp_list_resources.md @@ -0,0 +1,42 @@ +--- +title: thv mcp list resources +hide_title: true +description: Reference for ToolHive CLI command `thv mcp list resources` +last_update: + author: autogenerated +slug: thv_mcp_list_resources +mdx: + format: md +--- + +## thv mcp list resources + +List available resources from MCP server + +### Synopsis + +List all resources available from the specified MCP server. + +``` +thv mcp list resources [flags] +``` + +### Options + +``` + --format string Output format (json, text) (default "text") + -h, --help help for resources + --server string MCP server URL or name from ToolHive registry (required) + --timeout duration Connection timeout (default 30s) + --transport string Transport type (auto, sse, streamable-http) (default "auto") +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv mcp list](thv_mcp_list.md) - List MCP server capabilities diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_mcp_list_tools.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_mcp_list_tools.md new file mode 100644 index 00000000..166045ed --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_mcp_list_tools.md @@ -0,0 +1,42 @@ +--- +title: thv mcp list tools +hide_title: true +description: Reference for ToolHive CLI command `thv mcp list tools` +last_update: + author: autogenerated +slug: thv_mcp_list_tools +mdx: + format: md +--- + +## thv mcp list tools + +List available tools from MCP server + +### Synopsis + +List all tools available from the specified MCP server. + +``` +thv mcp list tools [flags] +``` + +### Options + +``` + --format string Output format (json, text) (default "text") + -h, --help help for tools + --server string MCP server URL or name from ToolHive registry (required) + --timeout duration Connection timeout (default 30s) + --transport string Transport type (auto, sse, streamable-http) (default "auto") +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv mcp list](thv_mcp_list.md) - List MCP server capabilities diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_mcp_serve.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_mcp_serve.md new file mode 100644 index 00000000..27f27323 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_mcp_serve.md @@ -0,0 +1,44 @@ +--- +title: thv mcp serve +hide_title: true +description: Reference for ToolHive CLI command `thv mcp serve` +last_update: + author: autogenerated +slug: thv_mcp_serve +mdx: + format: md +--- + +## thv mcp serve + +🧪 EXPERIMENTAL: Start an MCP server to control ToolHive + +### Synopsis + +🧪 EXPERIMENTAL: Start an MCP (Model Context Protocol) server that allows external clients to control ToolHive. +The server provides tools to search the registry, run MCP servers, and remove servers. +The server runs in privileged mode and can access the Docker socket directly. + +The port can be configured via the --port flag or the MCP_PORT environment variable. + +``` +thv mcp serve [flags] +``` + +### Options + +``` + -h, --help help for serve + --host string Host to listen on (default "localhost") + --port string Port to listen on (can also be set via MCP_PORT env var) (default "4483") +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv mcp](thv_mcp.md) - Interact with MCP servers for debugging diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_proxy.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_proxy.md new file mode 100644 index 00000000..d3592fff --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_proxy.md @@ -0,0 +1,148 @@ +--- +title: thv proxy +hide_title: true +description: Reference for ToolHive CLI command `thv proxy` +last_update: + author: autogenerated +slug: thv_proxy +mdx: + format: md +--- + +## thv proxy + +Create a transparent proxy for an MCP server with authentication support + +### Synopsis + +Create a transparent HTTP proxy that forwards requests to an MCP server endpoint. + +This command starts a standalone proxy without creating a workload, providing: + +- Transparent request forwarding to the target MCP server +- Optional OAuth/OIDC authentication to remote MCP servers +- Automatic authentication detection via WWW-Authenticate headers +- OIDC-based access control for incoming proxy requests +- Secure credential handling via files or environment variables +- Dynamic client registration (RFC 7591) for automatic OAuth client setup + +#### Authentication modes + +The proxy supports multiple authentication scenarios: + +1. No Authentication: Simple transparent forwarding +2. Outgoing Authentication: Authenticate to remote MCP servers using OAuth/OIDC +3. Incoming Authentication: Protect the proxy endpoint with OIDC validation +4. Bidirectional: Both incoming and outgoing authentication + +#### OAuth client secret sources + +OAuth client secrets can be provided via (in order of precedence): + +1. --remote-auth-client-secret flag (not recommended for production) +2. --remote-auth-client-secret-file flag (secure file-based approach) +3. TOOLHIVE_REMOTE_OAUTH_CLIENT_SECRET environment variable + +#### Dynamic client registration + +When no client credentials are provided, the proxy automatically registers an OAuth client +with the authorization server using RFC 7591 dynamic client registration: + +- No need to pre-configure client ID and secret +- Automatically discovers registration endpoint via OIDC +- Supports PKCE flow for enhanced security + +#### Examples + +Basic transparent proxy: + + thv proxy my-server --target-uri http://localhost:8080 + +Proxy with OIDC authentication to remote server: + + thv proxy my-server --target-uri https://api.example.com \ + --remote-auth --remote-auth-issuer https://auth.example.com \ + --remote-auth-client-id my-client-id \ + --remote-auth-client-secret-file /path/to/secret + +Proxy with non-OIDC OAuth authentication to remote server: + + thv proxy my-server --target-uri https://api.example.com \ + --remote-auth \ + --remote-auth-authorize-url https://auth.example.com/oauth/authorize \ + --remote-auth-token-url https://auth.example.com/oauth/token \ + --remote-auth-client-id my-client-id \ + --remote-auth-client-secret-file /path/to/secret + +Proxy with OIDC protection for incoming requests: + + thv proxy my-server --target-uri http://localhost:8080 \ + --oidc-issuer https://auth.example.com \ + --oidc-audience my-audience + +Auto-detect authentication requirements: + + thv proxy my-server --target-uri https://protected-api.com \ + --remote-auth-client-id my-client-id + +Dynamic client registration (automatic OAuth client setup): + + thv proxy my-server --target-uri https://protected-api.com \ + --remote-auth --remote-auth-issuer https://auth.example.com + +``` +thv proxy [flags] SERVER_NAME +``` + +### Options + +``` + -h, --help help for proxy + --host string Host for the HTTP proxy to listen on (IP or hostname) (default "127.0.0.1") + --oidc-audience string Expected audience for the token + --oidc-client-id string OIDC client ID + --oidc-client-secret string OIDC client secret (optional, for introspection) + --oidc-introspection-url string URL for token introspection endpoint + --oidc-issuer string OIDC issuer URL (e.g., https://accounts.google.com) + --oidc-jwks-url string URL to fetch the JWKS from + --oidc-scopes strings OAuth scopes to advertise in the well-known endpoint (RFC 9728, defaults to 'openid' if not specified) + --port int Port for the HTTP proxy to listen on (host port) + --remote-auth Enable OAuth/OIDC authentication to remote MCP server (default false) + --remote-auth-authorize-url string OAuth authorization endpoint URL (alternative to --remote-auth-issuer for non-OIDC OAuth) + --remote-auth-bearer-token string Bearer token for remote server authentication (alternative to OAuth) + --remote-auth-bearer-token-file string Path to file containing bearer token (alternative to --remote-auth-bearer-token) + --remote-auth-callback-port int Port for OAuth callback server during remote authentication (default 8666) + --remote-auth-client-id string OAuth client ID for remote server authentication (optional if the authorization server supports dynamic client registration (RFC 7591)) + --remote-auth-client-secret string OAuth client secret for remote server authentication (optional if the authorization server supports dynamic client registration (RFC 7591) or if using PKCE) + --remote-auth-client-secret-file string Path to file containing OAuth client secret (alternative to --remote-auth-client-secret) (optional if the authorization server supports dynamic client registration (RFC 7591) or if using PKCE) + --remote-auth-issuer string OAuth/OIDC issuer URL for remote server authentication (e.g., https://accounts.google.com) + --remote-auth-resource string OAuth 2.0 resource indicator (RFC 8707) + --remote-auth-scopes strings OAuth scopes to request for remote server authentication (defaults: OIDC uses 'openid,profile,email') + --remote-auth-skip-browser Skip opening browser for remote server OAuth flow (default false) + --remote-auth-timeout duration Timeout for OAuth authentication flow (e.g., 30s, 1m, 2m30s) (default 30s) + --remote-auth-token-url string OAuth token endpoint URL (alternative to --remote-auth-issuer for non-OIDC OAuth) + --remote-forward-headers stringArray Headers to inject into requests to remote server (format: Name=Value, can be repeated) + --remote-forward-headers-secret stringArray Headers with secret values from ToolHive secrets manager (format: Name=secret-name, can be repeated) + --resource-url string Explicit resource URL for OAuth discovery endpoint (RFC 9728) + --target-uri string URI for the target MCP server (e.g., http://localhost:8080) (required) + --token-exchange-audience string Target audience for exchanged tokens + --token-exchange-client-id string OAuth client ID for token exchange operations + --token-exchange-client-secret string OAuth client secret for token exchange operations + --token-exchange-client-secret-file string Path to file containing OAuth client secret for token exchange (alternative to --token-exchange-client-secret) + --token-exchange-header-name string Custom header name for injecting exchanged token (default: replaces Authorization header) + --token-exchange-scopes strings Scopes to request for exchanged tokens + --token-exchange-subject-token-type string Type of subject token to exchange. Accepts: access_token (default), id_token (required for Google STS) + --token-exchange-url string OAuth 2.0 token exchange endpoint URL (enables token exchange when provided) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers +- [thv proxy stdio](thv_proxy_stdio.md) - Create a stdio-based proxy for an MCP server +- [thv proxy tunnel](thv_proxy_tunnel.md) - Create a tunnel proxy for exposing internal endpoints diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_proxy_stdio.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_proxy_stdio.md new file mode 100644 index 00000000..1ae9df8b --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_proxy_stdio.md @@ -0,0 +1,41 @@ +--- +title: thv proxy stdio +hide_title: true +description: Reference for ToolHive CLI command `thv proxy stdio` +last_update: + author: autogenerated +slug: thv_proxy_stdio +mdx: + format: md +--- + +## thv proxy stdio + +Create a stdio-based proxy for an MCP server + +### Synopsis + +Create a stdio-based proxy that connects stdin/stdout to a target MCP server. + +Example: +thv proxy stdio my-workload + +``` +thv proxy stdio WORKLOAD-NAME [flags] +``` + +### Options + +``` + -h, --help help for stdio +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv proxy](thv_proxy.md) - Create a transparent proxy for an MCP server with authentication support diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_proxy_tunnel.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_proxy_tunnel.md new file mode 100644 index 00000000..1cf94924 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_proxy_tunnel.md @@ -0,0 +1,61 @@ +--- +title: thv proxy tunnel +hide_title: true +description: Reference for ToolHive CLI command `thv proxy tunnel` +last_update: + author: autogenerated +slug: thv_proxy_tunnel +mdx: + format: md +--- + +## thv proxy tunnel + +Create a tunnel proxy for exposing internal endpoints + +### Synopsis + +Create a tunnel proxy for exposing internal endpoints. + + TARGET may be either: + +• a URL (http://..., https://...) -> used directly as the target URI +• a workload name -> resolved to its URL + +Examples: +thv proxy tunnel http://localhost:8080 my-server --tunnel-provider ngrok +thv proxy tunnel my-workload my-server --tunnel-provider ngrok + +Flags: +--tunnel-provider string The provider to use for the tunnel (e.g., "ngrok") - mandatory +--provider-args string JSON object with provider-specific arguments: auth-token (mandatory), +url, pooling, traffic-policy-file +--dry-run If set, only validate the configuration without starting the tunnel + +Examples: +thv proxy tunnel --tunnel-provider ngrok --provider-args '{"auth-token": "your-token", +"url": "https://example.com", "pooling": true}' http://localhost:8080 my-server +thv proxy tunnel --tunnel-provider ngrok --provider-args '{"auth-token": "your-token", +"traffic-policy-file": "/path/to/policy.yml"}' my-workload my-server + +``` +thv proxy tunnel [flags] TARGET SERVER_NAME +``` + +### Options + +``` + -h, --help help for tunnel + --provider-args string JSON object with provider-specific arguments (default "{}") + --tunnel-provider string The provider to use for the tunnel (e.g., 'ngrok') - mandatory +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv proxy](thv_proxy.md) - Create a transparent proxy for an MCP server with authentication support diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_registry.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_registry.md new file mode 100644 index 00000000..eff5f17e --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_registry.md @@ -0,0 +1,38 @@ +--- +title: thv registry +hide_title: true +description: Reference for ToolHive CLI command `thv registry` +last_update: + author: autogenerated +slug: thv_registry +mdx: + format: md +--- + +## thv registry + +Manage MCP server registry + +### Synopsis + +Manage the MCP server registry, including listing and getting information about available MCP servers. + +### Options + +``` + -h, --help help for registry +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers +- [thv registry info](thv_registry_info.md) - Get information about an MCP server +- [thv registry list](thv_registry_list.md) - List available MCP servers +- [thv registry login](thv_registry_login.md) - Authenticate with the configured registry +- [thv registry logout](thv_registry_logout.md) - Clear cached registry credentials diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_registry_info.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_registry_info.md new file mode 100644 index 00000000..3b11e291 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_registry_info.md @@ -0,0 +1,40 @@ +--- +title: thv registry info +hide_title: true +description: Reference for ToolHive CLI command `thv registry info` +last_update: + author: autogenerated +slug: thv_registry_info +mdx: + format: md +--- + +## thv registry info + +Get information about an MCP server + +### Synopsis + +Get detailed information about a specific MCP server in the registry. + +``` +thv registry info [server] [flags] +``` + +### Options + +``` + --format string Output format (json, text) (default "text") + -h, --help help for info + --refresh Force refresh registry cache +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv registry](thv_registry.md) - Manage MCP server registry diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_registry_list.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_registry_list.md new file mode 100644 index 00000000..ad5c708c --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_registry_list.md @@ -0,0 +1,40 @@ +--- +title: thv registry list +hide_title: true +description: Reference for ToolHive CLI command `thv registry list` +last_update: + author: autogenerated +slug: thv_registry_list +mdx: + format: md +--- + +## thv registry list + +List available MCP servers + +### Synopsis + +List all available MCP servers in the registry. + +``` +thv registry list [flags] +``` + +### Options + +``` + --format string Output format (json, text) (default "text") + -h, --help help for list + --refresh Force refresh registry cache +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv registry](thv_registry.md) - Manage MCP server registry diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_registry_login.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_registry_login.md new file mode 100644 index 00000000..a7a613e9 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_registry_login.md @@ -0,0 +1,51 @@ +--- +title: thv registry login +hide_title: true +description: Reference for ToolHive CLI command `thv registry login` +last_update: + author: autogenerated +slug: thv_registry_login +mdx: + format: md +--- + +## thv registry login + +Authenticate with the configured registry + +### Synopsis + +Perform an interactive OAuth login against the configured registry. + +If the registry URL or OAuth configuration (issuer, client-id) are not yet +saved in config, you can supply them as flags and they will be persisted +before the login flow begins. + +Examples: +thv registry login +thv registry login --registry https://registry.example.com/api --issuer https://auth.example.com --client-id my-app + +``` +thv registry login [flags] +``` + +### Options + +``` + --audience string OAuth audience parameter for registry authentication (optional) + --client-id string OAuth client ID for registry authentication + -h, --help help for login + --issuer string OIDC issuer URL for registry authentication + --registry string Registry URL + --scopes strings OAuth scopes for registry authentication (defaults to openid,offline_access) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv registry](thv_registry.md) - Manage MCP server registry diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_registry_logout.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_registry_logout.md new file mode 100644 index 00000000..cb8e911d --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_registry_logout.md @@ -0,0 +1,38 @@ +--- +title: thv registry logout +hide_title: true +description: Reference for ToolHive CLI command `thv registry logout` +last_update: + author: autogenerated +slug: thv_registry_logout +mdx: + format: md +--- + +## thv registry logout + +Clear cached registry credentials + +### Synopsis + +Remove cached OAuth tokens for the configured registry. + +``` +thv registry logout [flags] +``` + +### Options + +``` + -h, --help help for logout +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv registry](thv_registry.md) - Manage MCP server registry diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_rm.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_rm.md new file mode 100644 index 00000000..f24e9d1c --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_rm.md @@ -0,0 +1,57 @@ +--- +title: thv rm +hide_title: true +description: Reference for ToolHive CLI command `thv rm` +last_update: + author: autogenerated +slug: thv_rm +mdx: + format: md +--- + +## thv rm + +Remove one or more MCP servers + +### Synopsis + +Remove one or more MCP servers managed by ToolHive. +Examples: + +# Remove a single MCP server + +thv rm filesystem + +# Remove multiple MCP servers + +thv rm filesystem github slack + +# Remove all workloads + +thv rm --all + +# Remove all workloads in a group + +thv rm --group production + +``` +thv rm [workload-name...] [flags] +``` + +### Options + +``` + --all Delete all workloads + -g, --group string Filter by group + -h, --help help for rm +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_run.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_run.md new file mode 100644 index 00000000..6d54b4f2 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_run.md @@ -0,0 +1,214 @@ +--- +title: thv run +hide_title: true +description: Reference for ToolHive CLI command `thv run` +last_update: + author: autogenerated +slug: thv_run +mdx: + format: md +--- + +## thv run + +Run an MCP server + +### Synopsis + +Run an MCP server with the specified name, image, or protocol scheme. + +ToolHive supports five ways to run an MCP server: + +1. From the registry: + + $ thv run server-name [-- args...] + + Looks up the server in the registry and uses its predefined settings + (transport, permissions, environment variables, etc.) + +2. From a container image: + + $ thv run ghcr.io/example/mcp-server:latest [-- args...] + + Runs the specified container image directly with the provided arguments + +3. Using a protocol scheme: + + $ thv run uvx://package-name [-- args...] + $ thv run npx://package-name [-- args...] + $ thv run go://package-name [-- args...] + $ thv run go://./local-path [-- args...] + + Automatically generates a container that runs the specified package + using either uvx (Python with uv package manager), npx (Node.js), + or go (Golang). For Go, you can also specify local paths starting + with './' or '../' to build and run local Go projects. + +4. From an exported configuration: + + $ thv run --from-config + + Runs an MCP server using a previously exported configuration file. + +5. Remote MCP server: + + $ thv run [--name ] + + Runs a remote MCP server as a workload, proxying requests to the specified URL. + This allows remote MCP servers to be managed like local workloads with full + support for client configuration, tool filtering, import/export, etc. + +#### Dynamic client registration + +When no client credentials are provided, ToolHive automatically registers an OAuth client +with the authorization server using RFC 7591 dynamic client registration: + +- No need to pre-configure client ID and secret +- Automatically discovers registration endpoint via OIDC +- Supports PKCE flow for enhanced security + +The container will be started with the specified transport mode and +permission profile. Additional configuration can be provided via flags. + +#### Network Configuration + +You can specify the network mode for the container using the --network flag: + +- Host networking: $ thv run --network host +- Custom network: $ thv run --network my-network +- Default (bridge): $ thv run + +The --network flag accepts any Docker-compatible network mode. + +Examples: + +# Run a server from the registry + +thv run filesystem + +# Run a server with custom arguments and toolsets + +thv run github -- --toolsets repos + +# Run from a container image + +thv run ghcr.io/github/github-mcp-server + +# Run using a protocol scheme (Python with uv) + +thv run uvx://mcp-server-git + +# Run using npx (Node.js) + +thv run npx://@modelcontextprotocol/server-everything + +# Run a server in a specific group + +thv run filesystem --group production + +# Run a remote GitHub MCP server with authentication + +thv run github-remote --remote-auth \ + --remote-auth-client-id \ + --remote-auth-client-secret + +``` +thv run [flags] SERVER_OR_IMAGE_OR_PROTOCOL [-- ARGS...] +``` + +### Options + +``` + --allow-docker-gateway Allow outbound connections to Docker gateway addresses (host.docker.internal, gateway.docker.internal, 172.17.0.1). Only applies when --isolate-network is set. These are blocked by default even when insecure_allow_all is enabled. + --audit-config string Path to the audit configuration file + --authz-config string Path to the authorization configuration file + --ca-cert string Path to a custom CA certificate file to use for container builds + --enable-audit Enable audit logging with default configuration (default false) + --endpoint-prefix string Path prefix to prepend to SSE endpoint URLs (e.g., /playwright) + -e, --env stringArray Environment variables to pass to the MCP server (format: KEY=VALUE) + --env-file string Load environment variables from a single file + --env-file-dir string Load environment variables from all files in a directory + -f, --foreground Run in foreground mode (block until container exits) (default false) + --from-config string Load configuration from exported file + --group string Name of the group this workload should belong to (default "default") + -h, --help help for run + --host string Host for the HTTP proxy to listen on (IP or hostname) (default "127.0.0.1") + --ignore-globally Load global ignore patterns from ~/.config/toolhive/thvignore (default true) + --image-verification string Set image verification mode (warn, enabled, disabled) (default "warn") + --isolate-network Isolate the container network from the host (default false) + --jwks-allow-private-ip Allow JWKS/OIDC endpoints on private IP addresses (use with caution) (default false) + --jwks-auth-token-file string Path to file containing bearer token for authenticating JWKS/OIDC requests + -l, --label stringArray Set labels on the container (format: key=value) + --name string Name of the MCP server (default to auto-generated from image) + --network string Connect the container to a network (e.g., 'host' for host networking) + --oidc-audience string Expected audience for the token + --oidc-client-id string OIDC client ID + --oidc-client-secret string OIDC client secret (optional, for introspection) + --oidc-insecure-allow-http Allow HTTP (non-HTTPS) OIDC issuers for local development/testing (WARNING: Insecure!) (default false) + --oidc-introspection-url string URL for token introspection endpoint + --oidc-issuer string OIDC issuer URL (e.g., https://accounts.google.com) + --oidc-jwks-url string URL to fetch the JWKS from + --oidc-scopes strings OAuth scopes to advertise in the well-known endpoint (RFC 9728, defaults to 'openid' if not specified) + --otel-custom-attributes string Custom resource attributes for OpenTelemetry in key=value format (e.g., server_type=prod,region=us-east-1,team=platform) + --otel-enable-prometheus-metrics-path Enable Prometheus-style /metrics endpoint on the main transport port (default false) + --otel-endpoint string OpenTelemetry OTLP endpoint URL (e.g., https://api.honeycomb.io) + --otel-env-vars stringArray Environment variable names to include in OpenTelemetry spans (comma-separated: ENV1,ENV2) + --otel-headers stringArray OpenTelemetry OTLP headers in key=value format (e.g., x-honeycomb-team=your-api-key) + --otel-insecure Connect to the OpenTelemetry endpoint using HTTP instead of HTTPS (default false) + --otel-metrics-enabled Enable OTLP metrics export (when OTLP endpoint is configured) (default true) + --otel-sampling-rate float OpenTelemetry trace sampling rate (0.0-1.0) (default 0.1) + --otel-service-name string OpenTelemetry service name (defaults to thv-) + --otel-tracing-enabled Enable distributed tracing (when OTLP endpoint is configured) (default true) + --otel-use-legacy-attributes Emit legacy attribute names alongside new OTEL semantic convention names (default true) (default true) + --permission-profile string Permission profile to use (none, network, or path to JSON file) (default is to use the permission profile from the registry or "network" if not part of the registry) + --print-resolved-overlays Debug: show resolved container paths for tmpfs overlays (default false) + --proxy-mode string Proxy mode for stdio (streamable-http or sse (deprecated, will be removed)) (default "streamable-http") + --proxy-port int Port for the HTTP proxy to listen on (host port) + -p, --publish stringArray Publish a container's port(s) to the host (format: hostPort:containerPort) + --remote-auth Enable OAuth/OIDC authentication to remote MCP server (default false) + --remote-auth-authorize-url string OAuth authorization endpoint URL (alternative to --remote-auth-issuer for non-OIDC OAuth) + --remote-auth-bearer-token string Bearer token for remote server authentication (alternative to OAuth) + --remote-auth-bearer-token-file string Path to file containing bearer token (alternative to --remote-auth-bearer-token) + --remote-auth-callback-port int Port for OAuth callback server during remote authentication (default 8666) + --remote-auth-client-id string OAuth client ID for remote server authentication (optional if the authorization server supports dynamic client registration (RFC 7591)) + --remote-auth-client-secret string OAuth client secret for remote server authentication (optional if the authorization server supports dynamic client registration (RFC 7591) or if using PKCE) + --remote-auth-client-secret-file string Path to file containing OAuth client secret (alternative to --remote-auth-client-secret) (optional if the authorization server supports dynamic client registration (RFC 7591) or if using PKCE) + --remote-auth-issuer string OAuth/OIDC issuer URL for remote server authentication (e.g., https://accounts.google.com) + --remote-auth-resource string OAuth 2.0 resource indicator (RFC 8707) + --remote-auth-scopes strings OAuth scopes to request for remote server authentication (defaults: OIDC uses 'openid,profile,email') + --remote-auth-skip-browser Skip opening browser for remote server OAuth flow (default false) + --remote-auth-timeout duration Timeout for OAuth authentication flow (e.g., 30s, 1m, 2m30s) (default 30s) + --remote-auth-token-url string OAuth token endpoint URL (alternative to --remote-auth-issuer for non-OIDC OAuth) + --remote-forward-headers stringArray Headers to inject into requests to remote MCP server (format: Name=Value, can be repeated) + --remote-forward-headers-secret stringArray Headers with secret values from ToolHive secrets manager (format: Name=secret-name, can be repeated) + --resource-url string Explicit resource URL for OAuth discovery endpoint (RFC 9728) + --runtime-add-package stringArray Add additional packages to install in the builder and runtime stages (can be repeated) + --runtime-image string Override the default base image for protocol schemes (e.g., golang:1.24-alpine, node:20-alpine, python:3.11-slim) + --secret stringArray Specify a secret to be fetched from the secrets manager and set as an environment variable (format: NAME,target=TARGET) + --target-host string Host to forward traffic to (only applicable to SSE or Streamable HTTP transport) (default "127.0.0.1") + --target-port int Port for the container to expose (only applicable to SSE or Streamable HTTP transport) + --thv-ca-bundle string Path to CA certificate bundle for ToolHive HTTP operations (JWKS, OIDC discovery, etc.) + --token-exchange-audience string Target audience for exchanged tokens + --token-exchange-client-id string OAuth client ID for token exchange operations + --token-exchange-client-secret string OAuth client secret for token exchange operations + --token-exchange-client-secret-file string Path to file containing OAuth client secret for token exchange (alternative to --token-exchange-client-secret) + --token-exchange-header-name string Custom header name for injecting exchanged token (default: replaces Authorization header) + --token-exchange-scopes strings Scopes to request for exchanged tokens + --token-exchange-subject-token-type string Type of subject token to exchange. Accepts: access_token (default), id_token (required for Google STS) + --token-exchange-url string OAuth 2.0 token exchange endpoint URL (enables token exchange when provided) + --tools stringArray Filter MCP server tools (comma-separated list of tool names) + --tools-override string Path to a JSON file containing overrides for MCP server tools names and descriptions + --transport string Transport mode (sse, streamable-http or stdio) + --trust-proxy-headers Trust X-Forwarded-* headers from reverse proxies (X-Forwarded-Proto, X-Forwarded-Host, X-Forwarded-Port, X-Forwarded-Prefix) (default false) + -v, --volume stringArray Mount a volume into the container (format: host-path:container-path[:ro]) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_runtime.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_runtime.md new file mode 100644 index 00000000..762b9aa7 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_runtime.md @@ -0,0 +1,31 @@ +--- +title: thv runtime +hide_title: true +description: Reference for ToolHive CLI command `thv runtime` +last_update: + author: autogenerated +slug: thv_runtime +mdx: + format: md +--- + +## thv runtime + +Commands related to the container runtime + +### Options + +``` + -h, --help help for runtime +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers +- [thv runtime check](thv_runtime_check.md) - Ping the container runtime diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_runtime_check.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_runtime_check.md new file mode 100644 index 00000000..32d21b52 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_runtime_check.md @@ -0,0 +1,39 @@ +--- +title: thv runtime check +hide_title: true +description: Reference for ToolHive CLI command `thv runtime check` +last_update: + author: autogenerated +slug: thv_runtime_check +mdx: + format: md +--- + +## thv runtime check + +Ping the container runtime + +### Synopsis + +Ensure the container runtime is responsive. + +``` +thv runtime check [flags] +``` + +### Options + +``` + -h, --help help for check + --timeout int Timeout in seconds for runtime checks (default: 30 seconds) (default 30) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv runtime](thv_runtime.md) - Commands related to the container runtime diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_search.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_search.md new file mode 100644 index 00000000..d965e160 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_search.md @@ -0,0 +1,39 @@ +--- +title: thv search +hide_title: true +description: Reference for ToolHive CLI command `thv search` +last_update: + author: autogenerated +slug: thv_search +mdx: + format: md +--- + +## thv search + +Search for MCP servers + +### Synopsis + +Search for MCP servers in the registry by name, description, or tags. + +``` +thv search [query] [flags] +``` + +### Options + +``` + --format string Output format (json or text) (default "text") + -h, --help help for search +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_secret.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_secret.md new file mode 100644 index 00000000..a6134ae6 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_secret.md @@ -0,0 +1,45 @@ +--- +title: thv secret +hide_title: true +description: Reference for ToolHive CLI command `thv secret` +last_update: + author: autogenerated +slug: thv_secret +mdx: + format: md +--- + +## thv secret + +Manage secrets + +### Synopsis + +Manage secrets using the configured secrets provider. + +The secret command provides subcommands to configure, store, retrieve, and manage secrets securely. + +Run "thv secret setup" first to configure a secrets provider before using any secret operations. + +### Options + +``` + -h, --help help for secret +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers +- [thv secret delete](thv_secret_delete.md) - Delete a secret +- [thv secret get](thv_secret_get.md) - Get a secret +- [thv secret list](thv_secret_list.md) - List all available secrets +- [thv secret provider](thv_secret_provider.md) - Set the secrets provider directly +- [thv secret reset-keyring](thv_secret_reset-keyring.md) - Reset the keyring password +- [thv secret set](thv_secret_set.md) - Set a secret +- [thv secret setup](thv_secret_setup.md) - Set up secrets provider diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_secret_delete.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_secret_delete.md new file mode 100644 index 00000000..fa6285d9 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_secret_delete.md @@ -0,0 +1,44 @@ +--- +title: thv secret delete +hide_title: true +description: Reference for ToolHive CLI command `thv secret delete` +last_update: + author: autogenerated +slug: thv_secret_delete +mdx: + format: md +--- + +## thv secret delete + +Delete a secret + +### Synopsis + +Remove a secret from the configured secrets provider. + +This command permanently deletes the specified secret from your secrets provider. +Once you delete a secret, you cannot recover it unless you have a backup. + +Note that some secrets providers may not support deletion operations. +If your provider is read-only or doesn't support deletion, this command returns an error. + +``` +thv secret delete [flags] +``` + +### Options + +``` + -h, --help help for delete +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv secret](thv_secret.md) - Manage secrets diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_secret_get.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_secret_get.md new file mode 100644 index 00000000..61f0b54e --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_secret_get.md @@ -0,0 +1,44 @@ +--- +title: thv secret get +hide_title: true +description: Reference for ToolHive CLI command `thv secret get` +last_update: + author: autogenerated +slug: thv_secret_get +mdx: + format: md +--- + +## thv secret get + +Get a secret + +### Synopsis + +Retrieve and display the value of a secret by name. + +This command fetches the specified secret from your configured secrets provider +and displays its value. The secret value prints to stdout, making it +suitable for use in scripts or command substitution. + +The secret must exist in your configured secrets provider, otherwise the command returns an error. + +``` +thv secret get [flags] +``` + +### Options + +``` + -h, --help help for get +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv secret](thv_secret.md) - Manage secrets diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_secret_list.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_secret_list.md new file mode 100644 index 00000000..e3df7c0b --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_secret_list.md @@ -0,0 +1,41 @@ +--- +title: thv secret list +hide_title: true +description: Reference for ToolHive CLI command `thv secret list` +last_update: + author: autogenerated +slug: thv_secret_list +mdx: + format: md +--- + +## thv secret list + +List all available secrets + +### Synopsis + +Display all secrets available in the configured secrets provider. + +This command shows the names of all secrets stored in your secrets provider. +If descriptions exist for the secrets, the command displays them alongside the names. + +``` +thv secret list [flags] +``` + +### Options + +``` + -h, --help help for list +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv secret](thv_secret.md) - Manage secrets diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_secret_provider.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_secret_provider.md new file mode 100644 index 00000000..79f525d6 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_secret_provider.md @@ -0,0 +1,48 @@ +--- +title: thv secret provider +hide_title: true +description: Reference for ToolHive CLI command `thv secret provider` +last_update: + author: autogenerated +slug: thv_secret_provider +mdx: + format: md +--- + +## thv secret provider + +Set the secrets provider directly + +### Synopsis + +Configure the secrets provider directly. + +Note: The "thv secret setup" command is recommended for interactive configuration. + +Use this command to set the secrets provider directly without interactive prompts, +making it suitable for scripted deployments and automation. + + Valid secrets providers: + - encrypted: Full read-write secrets provider using AES-256-GCM encryption + - 1password: Read-only secrets provider (requires OP_SERVICE_ACCOUNT_TOKEN) + - environment: Read-only secrets provider from TOOLHIVE_SECRET_* env vars + +``` +thv secret provider [flags] +``` + +### Options + +``` + -h, --help help for provider +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv secret](thv_secret.md) - Manage secrets diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_secret_reset-keyring.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_secret_reset-keyring.md new file mode 100644 index 00000000..09ce133b --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_secret_reset-keyring.md @@ -0,0 +1,53 @@ +--- +title: thv secret reset-keyring +hide_title: true +description: Reference for ToolHive CLI command `thv secret reset-keyring` +last_update: + author: autogenerated +slug: thv_secret_reset-keyring +mdx: + format: md +--- + +## thv secret reset-keyring + +Reset the keyring password + +### Synopsis + +Reset the keyring password used to encrypt secrets. + +This command resets the master password stored in your OS keyring that +encrypts and decrypts secrets when using the 'encrypted' secrets provider. + +Use this command if: + +- You've forgotten your keyring password +- You want to change your encryption password +- Your keyring has become corrupted + +Warning: Resetting the keyring password makes any existing encrypted secrets +inaccessible unless you remember the previous password. You will need to set up +your secrets again after resetting. + +This command only works with the 'encrypted' secrets provider. + +``` +thv secret reset-keyring [flags] +``` + +### Options + +``` + -h, --help help for reset-keyring +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv secret](thv_secret.md) - Manage secrets diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_secret_set.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_secret_set.md new file mode 100644 index 00000000..6264b5db --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_secret_set.md @@ -0,0 +1,60 @@ +--- +title: thv secret set +hide_title: true +description: Reference for ToolHive CLI command `thv secret set` +last_update: + author: autogenerated +slug: thv_secret_set +mdx: + format: md +--- + +## thv secret set + +Set a secret + +### Synopsis + +Create or update a secret with the specified name. + +This command supports two input methods for maximum flexibility: + +Piped input: + +When you pipe data to the command, it reads the secret value from stdin. +Examples: + + $ echo "my-secret-value" | thv secret set my-secret + $ cat secret-file.txt | thv secret set my-secret + +Interactive input: + +When you don't pipe data, the command prompts you to enter the secret value securely. +The input remains hidden for security. +Example: + + $ thv secret set my-secret + Enter secret value (input will be hidden): _ + +The command stores the secret securely using your configured secrets provider. +Note that some providers (like 1Password) are read-only and do not support setting secrets. + +``` +thv secret set [flags] +``` + +### Options + +``` + -h, --help help for set +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv secret](thv_secret.md) - Manage secrets diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_secret_setup.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_secret_setup.md new file mode 100644 index 00000000..55ab8083 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_secret_setup.md @@ -0,0 +1,49 @@ +--- +title: thv secret setup +hide_title: true +description: Reference for ToolHive CLI command `thv secret setup` +last_update: + author: autogenerated +slug: thv_secret_setup +mdx: + format: md +--- + +## thv secret setup + +Set up secrets provider + +### Synopsis + +Interactive setup for configuring a secrets provider. + +This command guides you through selecting and configuring a secrets provider +for storing and retrieving secrets. The setup process validates your +configuration and ensures the selected provider initializes properly. + + Available providers: + - encrypted: Stores secrets in an encrypted file using AES-256-GCM using the OS keyring + - 1password: Read-only access to 1Password secrets (requires OP_SERVICE_ACCOUNT_TOKEN environment variable) + - environment: Read-only access to secrets from TOOLHIVE_SECRET_* env vars + +Run this command before using any other secrets functionality. + +``` +thv secret setup [flags] +``` + +### Options + +``` + -h, --help help for setup +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv secret](thv_secret.md) - Manage secrets diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_serve.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_serve.md new file mode 100644 index 00000000..c7859ea2 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_serve.md @@ -0,0 +1,52 @@ +--- +title: thv serve +hide_title: true +description: Reference for ToolHive CLI command `thv serve` +last_update: + author: autogenerated +slug: thv_serve +mdx: + format: md +--- + +## thv serve + +Start the ToolHive API server + +### Synopsis + +Starts the ToolHive API server and listen for HTTP requests. + +``` +thv serve [flags] +``` + +### Options + +``` + --experimental-mcp EXPERIMENTAL: Enable embedded MCP server for controlling ToolHive + --experimental-mcp-host string EXPERIMENTAL: Host for the embedded MCP server (default "localhost") + --experimental-mcp-port string EXPERIMENTAL: Port for the embedded MCP server (default "4483") + -h, --help help for serve + --host string Host address to bind the server to (default "127.0.0.1") + --oidc-audience string Expected audience for the token + --oidc-client-id string OIDC client ID + --oidc-client-secret string OIDC client secret (optional, for introspection) + --oidc-introspection-url string URL for token introspection endpoint + --oidc-issuer string OIDC issuer URL (e.g., https://accounts.google.com) + --oidc-jwks-url string URL to fetch the JWKS from + --oidc-scopes strings OAuth scopes to advertise in the well-known endpoint (RFC 9728, defaults to 'openid' if not specified) + --openapi Enable OpenAPI documentation endpoints (/api/openapi.json and /api/doc) + --port int Port to bind the server to (default 8080) + --socket string UNIX socket path to bind the server to (overrides host and port if provided) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill.md new file mode 100644 index 00000000..8a95dd9e --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill.md @@ -0,0 +1,41 @@ +--- +title: thv skill +hide_title: true +description: Reference for ToolHive CLI command `thv skill` +last_update: + author: autogenerated +slug: thv_skill +mdx: + format: md +--- + +## thv skill + +Manage skills + +### Synopsis + +The skill command provides subcommands to manage skills. + +### Options + +``` + -h, --help help for skill +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers +- [thv skill build](thv_skill_build.md) - Build a skill +- [thv skill info](thv_skill_info.md) - Show skill details +- [thv skill install](thv_skill_install.md) - Install a skill +- [thv skill list](thv_skill_list.md) - List installed skills +- [thv skill push](thv_skill_push.md) - Push a built skill +- [thv skill uninstall](thv_skill_uninstall.md) - Uninstall a skill +- [thv skill validate](thv_skill_validate.md) - Validate a skill definition diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_build.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_build.md new file mode 100644 index 00000000..434749c2 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_build.md @@ -0,0 +1,41 @@ +--- +title: thv skill build +hide_title: true +description: Reference for ToolHive CLI command `thv skill build` +last_update: + author: autogenerated +slug: thv_skill_build +mdx: + format: md +--- + +## thv skill build + +Build a skill + +### Synopsis + +Build a skill from a local directory into an OCI artifact that can be pushed to a registry. + +On success, prints the OCI reference of the built artifact to stdout. + +``` +thv skill build [path] [flags] +``` + +### Options + +``` + -h, --help help for build + -t, --tag string OCI tag for the built artifact +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv skill](thv_skill.md) - Manage skills diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_builds.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_builds.md new file mode 100644 index 00000000..f82eb841 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_builds.md @@ -0,0 +1,40 @@ +--- +title: thv skill builds +hide_title: true +description: Reference for ToolHive CLI command `thv skill builds` +last_update: + author: autogenerated +slug: thv_skill_builds +mdx: + format: md +--- + +## thv skill builds + +List locally-built skill artifacts + +### Synopsis + +List all locally-built OCI skill artifacts stored in the local OCI store. + +``` +thv skill builds [flags] +``` + +### Options + +``` + --format string Output format (json, text) (default "text") + -h, --help help for builds +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv skill](thv_skill.md) - Manage skills +- [thv skill builds remove](thv_skill_builds_remove.md) - Remove a locally-built skill artifact diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_builds_remove.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_builds_remove.md new file mode 100644 index 00000000..09796324 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_builds_remove.md @@ -0,0 +1,38 @@ +--- +title: thv skill builds remove +hide_title: true +description: Reference for ToolHive CLI command `thv skill builds remove` +last_update: + author: autogenerated +slug: thv_skill_builds_remove +mdx: + format: md +--- + +## thv skill builds remove + +Remove a locally-built skill artifact + +### Synopsis + +Remove a locally-built OCI skill artifact and its blobs from the local OCI store. + +``` +thv skill builds remove [flags] +``` + +### Options + +``` + -h, --help help for remove +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv skill builds](thv_skill_builds.md) - List locally-built skill artifacts diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_info.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_info.md new file mode 100644 index 00000000..5a08f317 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_info.md @@ -0,0 +1,41 @@ +--- +title: thv skill info +hide_title: true +description: Reference for ToolHive CLI command `thv skill info` +last_update: + author: autogenerated +slug: thv_skill_info +mdx: + format: md +--- + +## thv skill info + +Show skill details + +### Synopsis + +Display detailed information about a skill, including metadata, version, and installation status. + +``` +thv skill info [skill-name] [flags] +``` + +### Options + +``` + --format string Output format (json, text) (default "text") + -h, --help help for info + --project-root string Project root path for project-scoped skills + --scope string Filter by scope (user, project) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv skill](thv_skill.md) - Manage skills diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_install.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_install.md new file mode 100644 index 00000000..84983bcb --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_install.md @@ -0,0 +1,44 @@ +--- +title: thv skill install +hide_title: true +description: Reference for ToolHive CLI command `thv skill install` +last_update: + author: autogenerated +slug: thv_skill_install +mdx: + format: md +--- + +## thv skill install + +Install a skill + +### Synopsis + +Install a skill by name or OCI reference. +The skill will be fetched from a remote registry and installed locally. + +``` +thv skill install [skill-name] [flags] +``` + +### Options + +``` + --client string Target client application (e.g. claude-code) + --force Overwrite existing skill directory + --group string Group to add the skill to after installation + -h, --help help for install + --project-root string Project root path for project-scoped installs + --scope string Installation scope (user, project) (default "user") +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv skill](thv_skill.md) - Manage skills diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_list.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_list.md new file mode 100644 index 00000000..62873cc5 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_list.md @@ -0,0 +1,43 @@ +--- +title: thv skill list +hide_title: true +description: Reference for ToolHive CLI command `thv skill list` +last_update: + author: autogenerated +slug: thv_skill_list +mdx: + format: md +--- + +## thv skill list + +List installed skills + +### Synopsis + +List all currently installed skills and their status. + +``` +thv skill list [flags] +``` + +### Options + +``` + --client string Filter by client application + --format string Output format (json, text) (default "text") + --group string Filter by group + -h, --help help for list + --project-root string Project root path for project-scoped skills + --scope string Filter by scope (user, project) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv skill](thv_skill.md) - Manage skills diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_push.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_push.md new file mode 100644 index 00000000..4d3b592e --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_push.md @@ -0,0 +1,38 @@ +--- +title: thv skill push +hide_title: true +description: Reference for ToolHive CLI command `thv skill push` +last_update: + author: autogenerated +slug: thv_skill_push +mdx: + format: md +--- + +## thv skill push + +Push a built skill + +### Synopsis + +Push a previously built skill artifact to a remote OCI registry. + +``` +thv skill push [reference] [flags] +``` + +### Options + +``` + -h, --help help for push +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv skill](thv_skill.md) - Manage skills diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_uninstall.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_uninstall.md new file mode 100644 index 00000000..891c9f07 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_uninstall.md @@ -0,0 +1,40 @@ +--- +title: thv skill uninstall +hide_title: true +description: Reference for ToolHive CLI command `thv skill uninstall` +last_update: + author: autogenerated +slug: thv_skill_uninstall +mdx: + format: md +--- + +## thv skill uninstall + +Uninstall a skill + +### Synopsis + +Remove a previously installed skill by name. + +``` +thv skill uninstall [skill-name] [flags] +``` + +### Options + +``` + -h, --help help for uninstall + --project-root string Project root path for project-scoped skills + --scope string Scope to uninstall from (user, project) (default "user") +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv skill](thv_skill.md) - Manage skills diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_validate.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_validate.md new file mode 100644 index 00000000..43b92ec6 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_skill_validate.md @@ -0,0 +1,39 @@ +--- +title: thv skill validate +hide_title: true +description: Reference for ToolHive CLI command `thv skill validate` +last_update: + author: autogenerated +slug: thv_skill_validate +mdx: + format: md +--- + +## thv skill validate + +Validate a skill definition + +### Synopsis + +Check that a skill definition in the given directory is valid and well-formed. + +``` +thv skill validate [path] [flags] +``` + +### Options + +``` + --format string Output format (json, text) (default "text") + -h, --help help for validate +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv skill](thv_skill.md) - Manage skills diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_start.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_start.md new file mode 100644 index 00000000..46b0423c --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_start.md @@ -0,0 +1,44 @@ +--- +title: thv start +hide_title: true +description: Reference for ToolHive CLI command `thv start` +last_update: + author: autogenerated +slug: thv_start +mdx: + format: md +--- + +## thv start + +Start (resume) a tooling server + +### Synopsis + +Start (or resume) a tooling server managed by ToolHive. +If the server is not running, it will be started. +The alias "thv restart" is kept for backward compatibility. +Supports both container-based and remote MCP servers. + +``` +thv start [workload-name] [flags] +``` + +### Options + +``` + -a, --all Restart all MCP servers + -f, --foreground Run the restarted workload in foreground mode (default false) + -g, --group string Filter by group + -h, --help help for start +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_status.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_status.md new file mode 100644 index 00000000..62528a4a --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_status.md @@ -0,0 +1,39 @@ +--- +title: thv status +hide_title: true +description: Reference for ToolHive CLI command `thv status` +last_update: + author: autogenerated +slug: thv_status +mdx: + format: md +--- + +## thv status + +Show detailed status of an MCP server + +### Synopsis + +Display detailed status information for a specific MCP server managed by ToolHive. + +``` +thv status [workload-name] [flags] +``` + +### Options + +``` + --format string Output format (json or text) (default "text") + -h, --help help for status +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_stop.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_stop.md new file mode 100644 index 00000000..f128a3d6 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_stop.md @@ -0,0 +1,57 @@ +--- +title: thv stop +hide_title: true +description: Reference for ToolHive CLI command `thv stop` +last_update: + author: autogenerated +slug: thv_stop +mdx: + format: md +--- + +## thv stop + +Stop one or more MCP servers + +### Synopsis + +Stop one or more running MCP servers managed by ToolHive. Examples: + +# Stop a single MCP server + +thv stop filesystem + +# Stop multiple MCP servers + +thv stop filesystem github slack + +# Stop all running MCP servers + +thv stop --all + +# Stop all servers in a group + +thv stop --group production + +``` +thv stop [workload-name...] [flags] +``` + +### Options + +``` + -a, --all Stop all running MCP servers + -g, --group string Filter by group + -h, --help help for stop + --timeout int Timeout in seconds before forcibly stopping the workload (default 30) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers diff --git a/versioned_docs/version-1.1/toolhive/reference/cli/thv_version.md b/versioned_docs/version-1.1/toolhive/reference/cli/thv_version.md new file mode 100644 index 00000000..8ec32468 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/cli/thv_version.md @@ -0,0 +1,40 @@ +--- +title: thv version +hide_title: true +description: Reference for ToolHive CLI command `thv version` +last_update: + author: autogenerated +slug: thv_version +mdx: + format: md +--- + +## thv version + +Show the version of ToolHive + +### Synopsis + +Display detailed version information about ToolHive, including version number, git commit, build date, and Go version. + +``` +thv version [flags] +``` + +### Options + +``` + --format string Output format (json or text) (default "text") + -h, --help help for version + --json Output version information as JSON (deprecated, use --format instead) +``` + +### Options inherited from parent commands + +``` + --debug Enable debug mode +``` + +### SEE ALSO + +- [thv](thv.md) - ToolHive (thv) is a lightweight, secure, and fast manager for MCP servers diff --git a/versioned_docs/version-1.1/toolhive/reference/client-compatibility.mdx b/versioned_docs/version-1.1/toolhive/reference/client-compatibility.mdx new file mode 100644 index 00000000..454cdb91 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/client-compatibility.mdx @@ -0,0 +1,375 @@ +--- +title: Client compatibility +description: Overview of supported AI clients and their compatibility with ToolHive. +--- + +This page shows which AI clients work with ToolHive and how they connect to MCP +servers. + +## Supported clients + +We've tested ToolHive with these clients: + +| Client | Supported | Auto-configuration | Skills support | Notes | +| -------------------------- | :-------: | :----------------: | :------------: | ------------------------------------------- | +| GitHub Copilot (VS Code) | ✅ | ✅ | ❌ | v1.102+ or Insiders version ([see note][3]) | +| Claude Code | ✅ | ✅ | ✅ | v1.0.27+ | +| Cursor | ✅ | ✅ | ❌ | v0.50.0+ | +| Google Antigravity | ✅ | ✅ | ❌ | | +| Gemini CLI | ✅ | ✅ | ❌ | | +| Cline (VS Code) | ✅ | ✅ | ❌ | v3.17.10+ | +| Continue (VS Code) | ✅ | ✅ | ❌ | v1.0.14+ | +| Continue (JetBrains) | ✅ | ✅ | ❌ | v1.0.23+ | +| Goose | ✅ | ✅ | ❌ | | +| Kiro | ✅ | ✅ | ❌ | v0.5.0+ | +| LM Studio | ✅ | ✅ | ❌ | v0.3.17+ | +| Mistral Vibe | ✅ | ✅ | ❌ | | +| OpenAI Codex | ✅ | ✅ | ✅ | | +| OpenCode | ✅ | ✅ | ✅ | | +| Roo Code (VS Code) | ✅ | ✅ | ❌ | v3.19.2+ | +| Sourcegraph Amp CLI | ✅ | ✅ | ❌ | | +| Sourcegraph Amp (VS Code) | ✅ | ✅ | ❌ | | +| Sourcegraph Amp (Cursor) | ✅ | ✅ | ❌ | | +| Sourcegraph Amp (Windsurf) | ✅ | ✅ | ❌ | | +| Trae IDE | ✅ | ✅ | ❌ | v1.4.1+ | +| VS Code Server | ✅ | ✅ | ❌ | | +| Windsurf IDE | ✅ | ✅ | ❌ | | +| Windsurf (JetBrains) | ✅ | ✅ | ❌ | | +| Zed | ✅ | ✅ | ❌ | v0.214.5+ | +| ChatGPT Desktop | ✅ | ❌ | ❌ | See [STDIO-only client configuration][4] | +| Claude Desktop | ✅ | ❌ | ❌ | See [STDIO-only client configuration][4] | +| GitHub Copilot (JetBrains) | ✅ | ❌ | ❌ | v1.5.47+ | +| PydanticAI | ✅ | ❌ | ❌ | v0.2.18+ | + +[3]: #vs-code-with-copilot +[4]: #stdio-only-client-configuration + +The minimum versions listed are the earliest versions that support the +Streamable HTTP transport protocol. + +You can also use other clients and development libraries that support the SSE +and/or Streamable HTTP protocol, but you'll need to configure them manually. + +## Client requirements + +To work with ToolHive, your client needs to: + +1. Support the Model Context Protocol (MCP) +2. Connect to MCP servers using the server-sent events (SSE) or Streamable HTTP + transport protocol +3. Have the correct MCP server URL configured + +## Automatic configuration support + +ToolHive can automatically configure supported clients to connect to MCP +servers. See the +[UI client configuration](../guides-ui/client-configuration.mdx) or +[CLI client configuration guide](../guides-cli/client-configuration.mdx) for +more details. + +Check the table above to see which clients support automatic configuration. + +For other clients, you'll need to set up the MCP server URL yourself. + +## Configuration locations + +ToolHive manages client configurations in their standard locations. + +### VS Code with Copilot + +GitHub Copilot in VS Code stores its +[global MCP configuration](https://code.visualstudio.com/docs/copilot/chat/mcp-servers#_add-an-mcp-server-to-your-user-settings) +in your VS Code user settings file. + +:::note + +VS Code versions prior to v1.102 stored MCP server settings in the main +`settings.json` file. As of v1.102, this has moved to a dedicated `mcp.json` +file. + +ToolHive v0.2.0 and works with VS Code 1.102+ and updates the `mcp.json` file +when you run an MCP server. + +If you're using an older version of either ToolHive or VS Code, automatic client +configuration will not work as expected and you will need to manually update +your configurations. We recommend upgrading both for the best experience. + +::: + +**Standard version**: + +- **macOS**: `~/Library/Application Support/Code/User/mcp.json` +- **Linux**: `~/.config/Code/User/mcp.json` + +**Insiders edition**: + +- **macOS**: `~/Library/Application Support/Code - Insiders/User/mcp.json` +- **Linux**: `~/.config/Code - Insiders/User/mcp.json` + +Example configuration: + +```json +{ + "servers": { + "github": { "url": "http://localhost:19046/mcp", "type": "http" }, + "fetch": { "url": "http://localhost:43832/mcp", "type": "http" }, + "sqlite": { "url": "http://localhost:51712/sse#sqlite", "type": "sse" } + } +} +``` + +When you register VS Code as a client, ToolHive automatically updates the global +MCP configuration file whenever you run an MCP server. You can also +[configure project-specific MCP servers](https://code.visualstudio.com/docs/copilot/chat/mcp-servers#_add-an-mcp-server-to-your-workspace) +by creating a `.vscode/mcp.json` file in your project directory. + +### VS Code Server + +[VS Code Server](https://code.visualstudio.com/docs/remote/vscode-server) is the +server-side component that enables remote development with Visual Studio Code. +It stores its global MCP configuration in a JSON file on the remote server. + +- **All platforms**: `~/.vscode-server/data/User/mcp.json` + +Example configuration: + +```json +{ + "servers": { + "github": { "url": "http://localhost:19046/mcp", "type": "http" }, + "fetch": { "url": "http://localhost:43832/mcp", "type": "http" }, + "sqlite": { "url": "http://localhost:51712/sse#sqlite", "type": "sse" } + } +} +``` + +When you register VS Code Server as a client, ToolHive automatically updates the +global MCP configuration file whenever you run an MCP server. + +### Cursor + +[Cursor](https://cursor.so/) stores its global MCP configuration in a JSON file +in your home directory. + +- **All platforms**: `~/.cursor/mcp.json` + +Example configuration: + +```json +{ + "mcpServers": { + "github": { "url": "http://localhost:19046/mcp" }, + "fetch": { "url": "http://localhost:43832/mcp" }, + "sqlite": { "url": "http://localhost:51712/sse#sqlite" } + } +} +``` + +Cursor automatically detects the transport mode for HTTP MCP servers. + +When you register Cursor as a client, ToolHive automatically updates the global +MCP configuration file whenever you run an MCP server. You can also +[configure project-specific MCP servers](https://docs.cursor.com/context/model-context-protocol#configuration-locations) +by creating a `.cursor/mcp.json` file in your project directory. + +### Claude Code + +[Claude Code](https://www.anthropic.com/claude-code) stores its global (user +scope) MCP configuration in a JSON file in your home directory. + +- **All platforms**: `~/.claude.json` + +Example configuration: + +```json +{ + // Other Claude Code settings... + + "mcpServers": { + "github": { "url": "http://localhost:19046/mcp", "type": "http" }, + "fetch": { "url": "http://localhost:43832/mcp", "type": "http" }, + "sqlite": { "url": "http://localhost:51712/sse#sqlite", "type": "sse" } + } +} +``` + +When you register Claude Code as a client, ToolHive automatically updates the +global MCP configuration file whenever you run an MCP server. You can also +[configure project-specific MCP servers](https://docs.anthropic.com/en/docs/claude-code/mcp#understanding-mcp-server-scopes) +by creating a `.mcp.json` file in your project directory, or add servers using +the `claude` CLI: + +```bash +claude mcp add --scope --transport http fetch http://localhost:43832/mcp +``` + +### Roo Code and Cline + +[Roo Code](https://roocode.com/) (previously Roo Cline) and +[Cline](https://cline.bot/) store their global MCP configuration in their VS +Code extension settings directory. Both use the same configuration format. + +**Roo Code**: + +- **macOS**: + `~/Library/Application Support/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/mcp_settings.json` +- **Linux**: + `~/.config/Code/User/globalStorage/rooveterinaryinc.roo-cline/settings/mcp_settings.json` + +**Cline**: + +- **macOS**: + `~/Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json` +- **Linux**: + `~/.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings/cline_mcp_settings.json` + +Example configuration: + +```json +{ + "mcpServers": { + "github": { + "url": "http://localhost:19046/mcp", + "type": "streamable-http" + }, + "fetch": { "url": "http://localhost:43832/mcp", "type": "streamable-http" }, + "sqlite": { "url": "http://localhost:51712/sse#sqlite", "type": "sse" } + // Note: Cline uses "streamableHttp" instead of "streamable-http" + } +} +``` + +When you register Roo Code or Cline as a client, ToolHive automatically updates +the global MCP configuration file whenever you run an MCP server. With Roo Code, +you can also configure +[project-specific MCP servers](https://docs.roocode.com/features/mcp/using-mcp-in-roo#editing-mcp-settings-files) +by creating a `.roo/mcp.json` file in your project directory. + +### Continue + +The [Continue](https://continue.dev) extension stores its global MCP +configuration in a JSON file in your home directory. This file is used by both +the VS Code and JetBrains versions of Continue. + +- **All platforms**: `~/.continue/config.yaml` + +Example configuration: + +```yaml +mcpServers: + - name: github + type: streamable-http + url: http://localhost:19046/mcp + - name: fetch + type: streamable-http + url: http://localhost:43832/mcp + - name: sqlite + type: sse + url: http://localhost:51712/sse#sqlite +``` + +Continue supports SSE (`type: sse`) and Streamable HTTP +(`type: streamable-http`) transports. + +When you register Continue as a client, ToolHive automatically updates the +global MCP configuration file whenever you run an MCP server. You can also +configure project-specific MCP servers by creating a +`.continue/mcpServers/.yaml` file in your project directory. + +## Manual configuration + +If your client doesn't support automatic configuration, you'll need to set up +the MCP server URL manually. + +### Example: PydanticAI + +For [PydanticAI](https://ai.pydantic.dev/), set the MCP server URL in your code: + +```python +from pydantic_ai.mcp import MCPServerSSE +from pydantic_ai.mcp import MCPServerStreamableHTTP + +# For Streamable HTTP +server = MCPServerStreamableHTTP(url='http://localhost:43832/mcp') + +# For Server-Sent Events (SSE) +server = MCPServerSSE(url='http://localhost:51712/sse#sqlite') +``` + +### Example: Copilot for JetBrains IDEs + +For the +[GitHub Copilot plugin in JetBrains IDEs](https://plugins.jetbrains.com/plugin/17718-github-copilot) +(IntelliJ, Pydantic, etc.), edit your `~/.config/github-copilot/mcp.json` file +to include the MCP server URL: + +```json +{ + "servers": { + "github": { "url": "http://localhost:43832/mcp", "type": "http" }, + "sqlite": { "url": "http://localhost:51712/sse#sqlite", "type": "sse" } + } +} +``` + +Copilot for JetBrains supports SSE (`"type": "sse"`) and Streamable HTTP +(`"type": "streamable-http"`) transports. + +### STDIO-only client configuration + +Some MCP clients, like Claude Desktop, only support MCP servers that communicate +via STDIO transport. However, many popular MCP servers use Server-Sent Events +(SSE) or Streamable HTTP transport protocols. + +ToolHive's `thv proxy stdio` command transforms servers using SSE or Streamable +HTTP into STDIO-compatible processes, enabling these clients to work with any +MCP server. + +**How it works:** + +1. ToolHive runs the MCP server in its container with its default transport + (SSE, HTTP, or other) +2. The `thv proxy stdio` command sets up a STDIO-level wrapper pointing to that + existing server +3. Your client interacts with this wrapper just like any other STDIO MCP server + +**Example:** + +First, run an MCP server with SSE transport: + +```bash +thv run osv --transport sse --name osv +``` + +Then configure your client to use the proxy stdio command. For Claude Desktop, +update your configuration file (typically located at +`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS): + +```json +{ + "mcpServers": { + "osv": { + "command": "thv", + "args": ["proxy", "stdio", "osv"] + } + } +} +``` + +:::tip + +The workload name you pass to `thv proxy stdio` (in this case, `osv`) must match +the name you used when running the server with `thv run`. + +::: + +After restarting your client, the MCP server's tools will be available for use, +even though it uses SSE transport. + +For more details, see the +[`thv proxy stdio` CLI reference](../reference/cli/thv_proxy_stdio.md). + +## Related information + +- [Client configuration](../guides-cli/client-configuration.mdx) +- [Run MCP servers](../guides-cli/run-mcp-servers.mdx) diff --git a/versioned_docs/version-1.1/toolhive/reference/crd-spec.md b/versioned_docs/version-1.1/toolhive/reference/crd-spec.md new file mode 100644 index 00000000..83f32463 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/crd-spec.md @@ -0,0 +1,2658 @@ +--- +title: Kubernetes CRD reference +sidebar_label: CRD Reference +description: ToolHive Kubernetes Operator Custom Resource Definitions (CRDs) reference. +toc_max_heading_level: 4 +mdx: + format: md +--- + +## Packages + +- [toolhive.stacklok.dev/audit](#toolhivestacklokdevaudit) +- [toolhive.stacklok.dev/authtypes](#toolhivestacklokdevauthtypes) +- [toolhive.stacklok.dev/config](#toolhivestacklokdevconfig) +- [toolhive.stacklok.dev/telemetry](#toolhivestacklokdevtelemetry) +- [toolhive.stacklok.dev/v1alpha1](#toolhivestacklokdevv1alpha1) + +## toolhive.stacklok.dev/audit + +#### pkg.audit.Config + +Config represents the audit logging configuration. + +_Appears in:_ + +- [vmcp.config.Config](#vmcpconfigconfig) + +| Field | Description | Default | Validation | +| ---------------------------------- | --------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `enabled` _boolean_ | Enabled controls whether audit logging is enabled.
When true, enables audit logging with the configured options. | false | Optional: \{\}
| +| `component` _string_ | Component is the component name to use in audit events. | | Optional: \{\}
| +| `eventTypes` _string array_ | EventTypes specifies which event types to audit. If empty, all events are audited. | | Optional: \{\}
| +| `excludeEventTypes` _string array_ | ExcludeEventTypes specifies which event types to exclude from auditing.
This takes precedence over EventTypes. | | Optional: \{\}
| +| `includeRequestData` _boolean_ | IncludeRequestData determines whether to include request data in audit logs. | false | Optional: \{\}
| +| `includeResponseData` _boolean_ | IncludeResponseData determines whether to include response data in audit logs. | false | Optional: \{\}
| +| `maxDataSize` _integer_ | MaxDataSize limits the size of request/response data included in audit logs (in bytes). | 1024 | Optional: \{\}
| +| `logFile` _string_ | LogFile specifies the file path for audit logs. If empty, logs to stdout. | | Optional: \{\}
| + +## toolhive.stacklok.dev/authtypes + +#### auth.types.BackendAuthStrategy + +BackendAuthStrategy defines how to authenticate to a specific backend. + +This struct provides type-safe configuration for different authentication strategies +using HeaderInjection or TokenExchange fields based on the Type field. + +_Appears in:_ + +- [vmcp.config.OutgoingAuthConfig](#vmcpconfigoutgoingauthconfig) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | ------- | ---------- | +| `type` _string_ | Type is the auth strategy: "unauthenticated", "header_injection", "token_exchange", "upstream_inject" | | | +| `headerInjection` _[auth.types.HeaderInjectionConfig](#authtypesheaderinjectionconfig)_ | HeaderInjection contains configuration for header injection auth strategy.
Used when Type = "header_injection". | | | +| `tokenExchange` _[auth.types.TokenExchangeConfig](#authtypestokenexchangeconfig)_ | TokenExchange contains configuration for token exchange auth strategy.
Used when Type = "token_exchange". | | | +| `upstreamInject` _[auth.types.UpstreamInjectConfig](#authtypesupstreaminjectconfig)_ | UpstreamInject contains configuration for upstream inject auth strategy.
Used when Type = "upstream_inject". | | | + +#### auth.types.HeaderInjectionConfig + +HeaderInjectionConfig configures the header injection auth strategy. +This strategy injects a static or environment-sourced header value into requests. + +_Appears in:_ + +- [auth.types.BackendAuthStrategy](#authtypesbackendauthstrategy) + +| Field | Description | Default | Validation | +| ------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------- | +| `headerName` _string_ | HeaderName is the name of the header to inject (e.g., "Authorization"). | | | +| `headerValue` _string_ | HeaderValue is the static header value to inject.
Either HeaderValue or HeaderValueEnv should be set, not both. | | | +| `headerValueEnv` _string_ | HeaderValueEnv is the environment variable name containing the header value.
The value will be resolved at runtime from this environment variable.
Either HeaderValue or HeaderValueEnv should be set, not both. | | | + +#### auth.types.TokenExchangeConfig + +TokenExchangeConfig configures the OAuth 2.0 token exchange auth strategy. +This strategy exchanges incoming tokens for backend-specific tokens using RFC 8693. + +_Appears in:_ + +- [auth.types.BackendAuthStrategy](#authtypesbackendauthstrategy) + +| Field | Description | Default | Validation | +| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------- | +| `tokenUrl` _string_ | TokenURL is the OAuth token endpoint URL for token exchange. | | | +| `clientId` _string_ | ClientID is the OAuth client ID for the token exchange request. | | | +| `clientSecret` _string_ | ClientSecret is the OAuth client secret (use ClientSecretEnv for security). | | | +| `clientSecretEnv` _string_ | ClientSecretEnv is the environment variable name containing the client secret.
The value will be resolved at runtime from this environment variable. | | | +| `audience` _string_ | Audience is the target audience for the exchanged token. | | | +| `scopes` _string array_ | Scopes are the requested scopes for the exchanged token. | | | +| `subjectTokenType` _string_ | SubjectTokenType is the token type of the incoming subject token.
Defaults to "urn:ietf:params:oauth:token-type:access_token" if not specified. | | | +| `subjectProviderName` _string_ | SubjectProviderName selects which upstream provider's token to use as the
subject token. When set, the token is looked up from Identity.UpstreamTokens
instead of using Identity.Token. | | | + +#### auth.types.UpstreamInjectConfig + +UpstreamInjectConfig configures the upstream inject auth strategy. +This strategy uses the embedded authorization server to obtain and inject +upstream IDP tokens into backend requests. + +_Appears in:_ + +- [auth.types.BackendAuthStrategy](#authtypesbackendauthstrategy) + +| Field | Description | Default | Validation | +| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------- | +| `providerName` _string_ | ProviderName is the name of the upstream provider configured in the
embedded authorization server. Must match an entry in AuthServer.Upstreams. | | | + +## toolhive.stacklok.dev/config + +#### vmcp.config.AggregationConfig + +AggregationConfig defines tool aggregation, filtering, and conflict resolution strategies. + +Tool Visibility vs Routing: + +- ExcludeAllTools, per-workload ExcludeAll, and Filter control which tools are + advertised to MCP clients (visible in tools/list responses). +- ALL backend tools remain available in the internal routing table, allowing + composite tools to call hidden backend tools. +- This enables curated experiences where raw backend tools are hidden from + MCP clients but accessible through composite tool workflows. + +_Appears in:_ + +- [vmcp.config.Config](#vmcpconfigconfig) + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------- | +| `conflictResolution` _[pkg.vmcp.ConflictResolutionStrategy](#pkgvmcpconflictresolutionstrategy)_ | ConflictResolution defines the strategy for resolving tool name conflicts.
- prefix: Automatically prefix tool names with workload identifier
- priority: First workload in priority order wins
- manual: Explicitly define overrides for all conflicts | prefix | Enum: [prefix priority manual]
Optional: \{\}
| +| `conflictResolutionConfig` _[vmcp.config.ConflictResolutionConfig](#vmcpconfigconflictresolutionconfig)_ | ConflictResolutionConfig provides configuration for the chosen strategy. | | Optional: \{\}
| +| `tools` _[vmcp.config.WorkloadToolConfig](#vmcpconfigworkloadtoolconfig) array_ | Tools defines per-workload tool filtering and overrides. | | Optional: \{\}
| +| `excludeAllTools` _boolean_ | ExcludeAllTools hides all backend tools from MCP clients when true.
Hidden tools are NOT advertised in tools/list responses, but they ARE
available in the routing table for composite tools to use.
This enables the use case where you want to hide raw backend tools from
direct client access while exposing curated composite tool workflows. | | Optional: \{\}
| + +#### vmcp.config.AuthzConfig + +AuthzConfig configures authorization. + +_Appears in:_ + +- [vmcp.config.IncomingAuthConfig](#vmcpconfigincomingauthconfig) + +| Field | Description | Default | Validation | +| ------------------------- | ----------------------------------------------------------------- | ------- | ---------- | +| `type` _string_ | Type is the authz type: "cedar", "none" | | | +| `policies` _string array_ | Policies contains Cedar policy definitions (when Type = "cedar"). | | | + +#### vmcp.config.CircuitBreakerConfig + +CircuitBreakerConfig configures circuit breaker behavior. + +_Appears in:_ + +- [vmcp.config.FailureHandlingConfig](#vmcpconfigfailurehandlingconfig) + +| Field | Description | Default | Validation | +| ------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------- | +| `enabled` _boolean_ | Enabled controls whether circuit breaker is enabled. | false | Optional: \{\}
| +| `failureThreshold` _integer_ | FailureThreshold is the number of failures before opening the circuit.
Must be >= 1. | 5 | Minimum: 1
Optional: \{\}
| +| `timeout` _[vmcp.config.Duration](#vmcpconfigduration)_ | Timeout is the duration to wait before attempting to close the circuit.
Must be >= 1s to prevent thrashing. | 60s | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Type: string
Optional: \{\}
| + +#### vmcp.config.CompositeToolConfig + +CompositeToolConfig defines a composite tool workflow. +This matches the YAML structure from the proposal (lines 173-255). + +_Appears in:_ + +- [vmcp.config.Config](#vmcpconfigconfig) +- [api.v1alpha1.VirtualMCPCompositeToolDefinitionSpec](#apiv1alpha1virtualmcpcompositetooldefinitionspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------ | +| `name` _string_ | Name is the workflow name (unique identifier). | | | +| `description` _string_ | Description describes what the workflow does. | | | +| `parameters` _[pkg.json.Map](#pkgjsonmap)_ | Parameters defines input parameter schema in JSON Schema format.
Should be a JSON Schema object with "type": "object" and "properties".
Example:
\{
"type": "object",
"properties": \{
"param1": \{"type": "string", "default": "value"\},
"param2": \{"type": "integer"\}
\},
"required": ["param2"]
\}
We use json.Map rather than a typed struct because JSON Schema is highly
flexible with many optional fields (default, enum, minimum, maximum, pattern,
items, additionalProperties, oneOf, anyOf, allOf, etc.). Using json.Map
allows full JSON Schema compatibility without needing to define every possible
field, and matches how the MCP SDK handles inputSchema. | | Optional: \{\}
| +| `timeout` _[vmcp.config.Duration](#vmcpconfigduration)_ | Timeout is the maximum workflow execution time. | | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Type: string
| +| `steps` _[vmcp.config.WorkflowStepConfig](#vmcpconfigworkflowstepconfig) array_ | Steps are the workflow steps to execute. | | | +| `output` _[vmcp.config.OutputConfig](#vmcpconfigoutputconfig)_ | Output defines the structured output schema for this workflow.
If not specified, the workflow returns the last step's output (backward compatible). | | Optional: \{\}
| + +#### vmcp.config.CompositeToolRef + +CompositeToolRef defines a reference to a VirtualMCPCompositeToolDefinition resource. +The referenced resource must be in the same namespace as the VirtualMCPServer. + +_Appears in:_ + +- [vmcp.config.Config](#vmcpconfigconfig) + +| Field | Description | Default | Validation | +| --------------- | ----------------------------------------------------------------------------------------- | ------- | --------------------- | +| `name` _string_ | Name is the name of the VirtualMCPCompositeToolDefinition resource in the same namespace. | | Required: \{\}
| + +#### vmcp.config.Config + +Config is the unified configuration model for Virtual MCP Server. +This is platform-agnostic and used by both CLI and Kubernetes deployments. + +Platform-specific adapters (CLI YAML loader, Kubernetes CRD converter) +transform their native formats into this model. + +_Validation:_ + +- Type: object + +_Appears in:_ + +- [api.v1alpha1.VirtualMCPServerSpec](#apiv1alpha1virtualmcpserverspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `name` _string_ | Name is the virtual MCP server name. | | Optional: \{\}
| +| `groupRef` _string_ | Group references an existing MCPGroup that defines backend workloads.
In Kubernetes, the referenced MCPGroup must exist in the same namespace. | | Required: \{\}
| +| `backends` _[vmcp.config.StaticBackendConfig](#vmcpconfigstaticbackendconfig) array_ | Backends defines pre-configured backend servers for static mode.
When OutgoingAuth.Source is "inline", this field contains the full list of backend
servers with their URLs and transport types, eliminating the need for K8s API access.
When OutgoingAuth.Source is "discovered", this field is empty and backends are
discovered at runtime via Kubernetes API. | | Optional: \{\}
| +| `incomingAuth` _[vmcp.config.IncomingAuthConfig](#vmcpconfigincomingauthconfig)_ | IncomingAuth configures how clients authenticate to the virtual MCP server.
When using the Kubernetes operator, this is populated by the converter from
VirtualMCPServerSpec.IncomingAuth and any values set here will be superseded. | | Optional: \{\}
| +| `outgoingAuth` _[vmcp.config.OutgoingAuthConfig](#vmcpconfigoutgoingauthconfig)_ | OutgoingAuth configures how the virtual MCP server authenticates to backends.
When using the Kubernetes operator, this is populated by the converter from
VirtualMCPServerSpec.OutgoingAuth and any values set here will be superseded. | | Optional: \{\}
| +| `aggregation` _[vmcp.config.AggregationConfig](#vmcpconfigaggregationconfig)_ | Aggregation defines tool aggregation and conflict resolution strategies.
Supports ToolConfigRef for Kubernetes-native MCPToolConfig resource references. | | Optional: \{\}
| +| `compositeTools` _[vmcp.config.CompositeToolConfig](#vmcpconfigcompositetoolconfig) array_ | CompositeTools defines inline composite tool workflows.
Full workflow definitions are embedded in the configuration.
For Kubernetes, complex workflows can also reference VirtualMCPCompositeToolDefinition CRDs. | | Optional: \{\}
| +| `compositeToolRefs` _[vmcp.config.CompositeToolRef](#vmcpconfigcompositetoolref) array_ | CompositeToolRefs references VirtualMCPCompositeToolDefinition resources
for complex, reusable workflows. Only applicable when running in Kubernetes.
Referenced resources must be in the same namespace as the VirtualMCPServer. | | Optional: \{\}
| +| `operational` _[vmcp.config.OperationalConfig](#vmcpconfigoperationalconfig)_ | Operational configures operational settings. | | | +| `metadata` _object (keys:string, values:string)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `telemetry` _[pkg.telemetry.Config](#pkgtelemetryconfig)_ | Telemetry configures OpenTelemetry-based observability for the Virtual MCP server
including distributed tracing, OTLP metrics export, and Prometheus metrics endpoint. | | Optional: \{\}
| +| `audit` _[pkg.audit.Config](#pkgauditconfig)_ | Audit configures audit logging for the Virtual MCP server.
When present, audit logs include MCP protocol operations.
See audit.Config for available configuration options. | | Optional: \{\}
| +| `optimizer` _[vmcp.config.OptimizerConfig](#vmcpconfigoptimizerconfig)_ | Optimizer configures the MCP optimizer for context optimization on large toolsets.
When enabled, vMCP exposes only find_tool and call_tool operations to clients
instead of all backend tools directly. This reduces token usage by allowing
LLMs to discover relevant tools on demand rather than receiving all tool definitions. | | Optional: \{\}
| +| `sessionStorage` _[vmcp.config.SessionStorageConfig](#vmcpconfigsessionstorageconfig)_ | SessionStorage configures session storage for stateful horizontal scaling.
When provider is "redis", the operator injects Redis connection parameters
(address, db, keyPrefix) here. The Redis password is provided separately via
the THV_SESSION_REDIS_PASSWORD environment variable. | | Optional: \{\}
| + +#### vmcp.config.ConflictResolutionConfig + +ConflictResolutionConfig provides configuration for conflict resolution strategies. + +_Appears in:_ + +- [vmcp.config.AggregationConfig](#vmcpconfigaggregationconfig) + +| Field | Description | Default | Validation | +| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | -------------- | --------------------- | +| `prefixFormat` _string_ | PrefixFormat defines the prefix format for the "prefix" strategy.
Supports placeholders: \{workload\}, \{workload\}\_, \{workload\}. | \{workload\}\_ | Optional: \{\}
| +| `priorityOrder` _string array_ | PriorityOrder defines the workload priority order for the "priority" strategy. | | Optional: \{\}
| + +#### vmcp.config.ElicitationResponseConfig + +ElicitationResponseConfig defines how to handle user responses to elicitation requests. + +_Appears in:_ + +- [vmcp.config.WorkflowStepConfig](#vmcpconfigworkflowstepconfig) + +| Field | Description | Default | Validation | +| ----------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----------------------------------------------------------------- | +| `action` _string_ | Action defines the action to take when the user declines or cancels
- skip_remaining: Skip remaining steps in the workflow
- abort: Abort the entire workflow execution
- continue: Continue to the next step | abort | Enum: [skip_remaining abort continue]
Optional: \{\}
| + +#### vmcp.config.FailureHandlingConfig + +FailureHandlingConfig configures failure handling behavior. + +_Appears in:_ + +- [vmcp.config.OperationalConfig](#vmcpconfigoperationalconfig) + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------- | +| `healthCheckInterval` _[vmcp.config.Duration](#vmcpconfigduration)_ | HealthCheckInterval is the interval between health checks. | 30s | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Type: string
Optional: \{\}
| +| `unhealthyThreshold` _integer_ | UnhealthyThreshold is the number of consecutive failures before marking unhealthy. | 3 | Optional: \{\}
| +| `healthCheckTimeout` _[vmcp.config.Duration](#vmcpconfigduration)_ | HealthCheckTimeout is the maximum duration for a single health check operation.
Should be less than HealthCheckInterval to prevent checks from queuing up. | 10s | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Type: string
Optional: \{\}
| +| `statusReportingInterval` _[vmcp.config.Duration](#vmcpconfigduration)_ | StatusReportingInterval is the interval for reporting status updates to Kubernetes.
This controls how often the vMCP runtime reports backend health and phase changes.
Lower values provide faster status updates but increase API server load. | 30s | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Type: string
Optional: \{\}
| +| `partialFailureMode` _string_ | PartialFailureMode defines behavior when some backends are unavailable.
- fail: Fail entire request if any backend is unavailable
- best_effort: Continue with available backends | fail | Enum: [fail best_effort]
Optional: \{\}
| +| `circuitBreaker` _[vmcp.config.CircuitBreakerConfig](#vmcpconfigcircuitbreakerconfig)_ | CircuitBreaker configures circuit breaker behavior. | | Optional: \{\}
| + +#### vmcp.config.IncomingAuthConfig + +IncomingAuthConfig configures client authentication to the virtual MCP server. + +Note: When using the Kubernetes operator (VirtualMCPServer CRD), the +VirtualMCPServerSpec.IncomingAuth field is the authoritative source for +authentication configuration. The operator's converter will resolve the CRD's +IncomingAuth (which supports Kubernetes-native references like SecretKeyRef, +ConfigMapRef, etc.) and populate this IncomingAuthConfig with the resolved values. +Any values set here directly will be superseded by the CRD configuration. + +_Appears in:_ + +- [vmcp.config.Config](#vmcpconfigconfig) + +| Field | Description | Default | Validation | +| ----------------------------------------------------------- | ------------------------------------------------------ | ------- | ---------- | +| `type` _string_ | Type is the auth type: "oidc", "local", "anonymous" | | | +| `oidc` _[vmcp.config.OIDCConfig](#vmcpconfigoidcconfig)_ | OIDC contains OIDC configuration (when Type = "oidc"). | | | +| `authz` _[vmcp.config.AuthzConfig](#vmcpconfigauthzconfig)_ | Authz contains authorization configuration (optional). | | | + +#### vmcp.config.OIDCConfig + +OIDCConfig configures OpenID Connect authentication. + +_Appears in:_ + +- [vmcp.config.IncomingAuthConfig](#vmcpconfigincomingauthconfig) + +| Field | Description | Default | Validation | +| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------- | +| `issuer` _string_ | Issuer is the OIDC issuer URL. | | Pattern: `^https?://`
| +| `clientId` _string_ | ClientID is the OAuth client ID. | | | +| `clientSecretEnv` _string_ | ClientSecretEnv is the name of the environment variable containing the client secret.
This is the secure way to reference secrets - the actual secret value is never stored
in configuration files, only the environment variable name.
The secret value will be resolved from this environment variable at runtime. | | | +| `audience` _string_ | Audience is the required token audience. | | | +| `resource` _string_ | Resource is the OAuth 2.0 resource indicator (RFC 8707).
Used in WWW-Authenticate header and OAuth discovery metadata (RFC 9728).
If not specified, defaults to Audience. | | | +| `scopes` _string array_ | Scopes are the required OAuth scopes. | | | +| `protectedResourceAllowPrivateIp` _boolean_ | ProtectedResourceAllowPrivateIP allows protected resource endpoint on private IP addresses
Use with caution - only enable for trusted internal IDPs or testing | | | +| `jwksAllowPrivateIp` _boolean_ | JwksAllowPrivateIP allows OIDC discovery and JWKS fetches to private IP addresses.
Enable when the embedded auth server runs on a loopback address and
the OIDC middleware needs to fetch its JWKS from that address.
Use with caution - only enable for trusted internal IDPs or testing. | | | +| `insecureAllowHttp` _boolean_ | InsecureAllowHTTP allows HTTP (non-HTTPS) OIDC issuers for development/testing
WARNING: This is insecure and should NEVER be used in production | | | + +#### vmcp.config.OperationalConfig + +OperationalConfig contains operational settings. +OperationalConfig defines operational settings like timeouts and health checks. + +_Appears in:_ + +- [vmcp.config.Config](#vmcpconfigconfig) + +| Field | Description | Default | Validation | +| ----------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ----------------------------------------- | +| `logLevel` _string_ | LogLevel sets the logging level for the Virtual MCP server.
The only valid value is "debug" to enable debug logging.
When omitted or empty, the server uses info level logging. | | Enum: [debug]
Optional: \{\}
| +| `timeouts` _[vmcp.config.TimeoutConfig](#vmcpconfigtimeoutconfig)_ | Timeouts configures timeout settings. | | Optional: \{\}
| +| `failureHandling` _[vmcp.config.FailureHandlingConfig](#vmcpconfigfailurehandlingconfig)_ | FailureHandling configures failure handling behavior. | | Optional: \{\}
| + +#### vmcp.config.OptimizerConfig + +OptimizerConfig configures the MCP optimizer. +When enabled, vMCP exposes only find_tool and call_tool operations to clients +instead of all backend tools directly. + +_Appears in:_ + +- [vmcp.config.Config](#vmcpconfigconfig) + +| Field | Description | Default | Validation | +| ----------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------- | +| `embeddingService` _string_ | EmbeddingService is the full base URL of the embedding service endpoint
(e.g., http://my-embedding.default.svc.cluster.local:8080) for semantic
tool discovery.
In a Kubernetes environment, it is more convenient to use the
VirtualMCPServerSpec.EmbeddingServerRef field instead of setting this
directly. EmbeddingServerRef references an EmbeddingServer CRD by name,
and the operator automatically resolves the referenced resource's
Status.URL to populate this field. This provides managed lifecycle
(the operator watches the EmbeddingServer for readiness and URL changes)
and avoids hardcoding service URLs in the config. If both
EmbeddingServerRef and this field are set, EmbeddingServerRef takes
precedence and this value is overridden with a warning. | | Optional: \{\}
| +| `embeddingServiceTimeout` _[vmcp.config.Duration](#vmcpconfigduration)_ | EmbeddingServiceTimeout is the HTTP request timeout for calls to the embedding service.
Defaults to 30s if not specified. | 30s | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Type: string
Optional: \{\}
| +| `maxToolsToReturn` _integer_ | MaxToolsToReturn is the maximum number of tool results returned by a search query.
Defaults to 8 if not specified or zero. | | Maximum: 50
Minimum: 1
Optional: \{\}
| +| `hybridSearchSemanticRatio` _string_ | HybridSearchSemanticRatio controls the balance between semantic (meaning-based)
and keyword search results. 0.0 = all keyword, 1.0 = all semantic.
Defaults to "0.5" if not specified or empty.
Serialized as a string because CRDs do not support float types portably. | | Pattern: `^([0-9]*[.])?[0-9]+$`
Optional: \{\}
| +| `semanticDistanceThreshold` _string_ | SemanticDistanceThreshold is the maximum distance for semantic search results.
Results exceeding this threshold are filtered out from semantic search.
This threshold does not apply to keyword search.
Range: 0 = identical, 2 = completely unrelated.
Defaults to "1.0" if not specified or empty.
Serialized as a string because CRDs do not support float types portably. | | Pattern: `^([0-9]*[.])?[0-9]+$`
Optional: \{\}
| + +#### vmcp.config.OutgoingAuthConfig + +OutgoingAuthConfig configures backend authentication. + +Note: When using the Kubernetes operator (VirtualMCPServer CRD), the +VirtualMCPServerSpec.OutgoingAuth field is the authoritative source for +backend authentication configuration. The operator's converter will resolve +the CRD's OutgoingAuth (which supports Kubernetes-native references like +SecretKeyRef, ConfigMapRef, etc.) and populate this OutgoingAuthConfig with +the resolved values. Any values set here directly will be superseded by the +CRD configuration. + +_Appears in:_ + +- [vmcp.config.Config](#vmcpconfigconfig) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------- | +| `source` _string_ | Source defines how to discover backend auth: "inline", "discovered"
- inline: Explicit configuration in OutgoingAuth
- discovered: Auto-discover from backend MCPServer.externalAuthConfigRef (Kubernetes only) | | | +| `default` _[auth.types.BackendAuthStrategy](#authtypesbackendauthstrategy)_ | Default is the default auth strategy for backends without explicit config. | | | +| `backends` _object (keys:string, values:[auth.types.BackendAuthStrategy](#authtypesbackendauthstrategy))_ | Backends contains per-backend auth configuration. | | | + +#### vmcp.config.OutputConfig + +OutputConfig defines the structured output schema for a composite tool workflow. +This follows the same pattern as the Parameters field, defining both the +MCP output schema (type, description) and runtime value construction (value, default). + +_Appears in:_ + +- [vmcp.config.CompositeToolConfig](#vmcpconfigcompositetoolconfig) +- [api.v1alpha1.VirtualMCPCompositeToolDefinitionSpec](#apiv1alpha1virtualmcpcompositetooldefinitionspec) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `properties` _object (keys:string, values:[vmcp.config.OutputProperty](#vmcpconfigoutputproperty))_ | Properties defines the output properties.
Map key is the property name, value is the property definition. | | | +| `required` _string array_ | Required lists property names that must be present in the output. | | Optional: \{\}
| + +#### vmcp.config.OutputProperty + +OutputProperty defines a single output property. +For non-object types, Value is required. +For object types, either Value or Properties must be specified (but not both). + +_Appears in:_ + +- [vmcp.config.OutputConfig](#vmcpconfigoutputconfig) +- [vmcp.config.OutputProperty](#vmcpconfigoutputproperty) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------ | +| `type` _string_ | Type is the JSON Schema type: "string", "integer", "number", "boolean", "object", "array" | | Enum: [string integer number boolean object array]
Required: \{\}
| +| `description` _string_ | Description is a human-readable description exposed to clients and models | | Optional: \{\}
| +| `value` _string_ | Value is a template string for constructing the runtime value.
For object types, this can be a JSON string that will be deserialized.
Supports template syntax: \{\{.steps.step_id.output.field\}\}, \{\{.params.param_name\}\} | | Optional: \{\}
| +| `properties` _object (keys:string, values:[vmcp.config.OutputProperty](#vmcpconfigoutputproperty))_ | Properties defines nested properties for object types.
Each nested property has full metadata (type, description, value/properties). | | Schemaless: \{\}
Type: object
Optional: \{\}
| +| `default` _[pkg.json.Any](#pkgjsonany)_ | Default is the fallback value if template expansion fails.
Type coercion is applied to match the declared Type. | | Schemaless: \{\}
Optional: \{\}
| + +#### vmcp.config.SessionStorageConfig + +SessionStorageConfig configures session storage for stateful horizontal scaling. +The Redis password is not stored here; it is injected as the THV_SESSION_REDIS_PASSWORD +environment variable by the operator when spec.sessionStorage.passwordRef is set. + +_Appears in:_ + +- [vmcp.config.Config](#vmcpconfigconfig) + +| Field | Description | Default | Validation | +| -------------------- | ---------------------------------------------------------------------- | ------- | ------------------------------------------------ | +| `provider` _string_ | Provider is the session storage backend type. | | Enum: [memory redis]
Required: \{\}
| +| `address` _string_ | Address is the Redis server address (required when provider is redis). | | Optional: \{\}
| +| `db` _integer_ | DB is the Redis database number. | 0 | Minimum: 0
Optional: \{\}
| +| `keyPrefix` _string_ | KeyPrefix is an optional prefix for all Redis keys used by ToolHive. | | Optional: \{\}
| + +#### vmcp.config.StaticBackendConfig + +StaticBackendConfig defines a pre-configured backend server for static mode. +This allows vMCP to operate without Kubernetes API access by embedding all backend +information directly in the configuration. + +_Appears in:_ + +- [vmcp.config.Config](#vmcpconfigconfig) + +| Field | Description | Default | Validation | +| ------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------- | +| `name` _string_ | Name is the backend identifier.
Must match the backend name from the MCPGroup for auth config resolution. | | Required: \{\}
| +| `url` _string_ | URL is the backend's MCP server base URL. | | Pattern: `^https?://`
Required: \{\}
| +| `transport` _string_ | Transport is the MCP transport protocol: "sse" or "streamable-http"
Only network transports supported by vMCP client are allowed. | | Enum: [sse streamable-http]
Required: \{\}
| +| `metadata` _object (keys:string, values:string)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | Optional: \{\}
| + +#### vmcp.config.StepErrorHandling + +StepErrorHandling defines error handling behavior for workflow steps. + +_Appears in:_ + +- [vmcp.config.WorkflowStepConfig](#vmcpconfigworkflowstepconfig) + +| Field | Description | Default | Validation | +| ---------------------------------------------------------- | ------------------------------------------------------------------------------------ | ------- | --------------------------------------------------------------------------------------------------------- | +| `action` _string_ | Action defines the action to take on error | abort | Enum: [abort continue retry]
Optional: \{\}
| +| `retryCount` _integer_ | RetryCount is the maximum number of retries
Only used when Action is "retry" | | Optional: \{\}
| +| `retryDelay` _[vmcp.config.Duration](#vmcpconfigduration)_ | RetryDelay is the delay between retry attempts
Only used when Action is "retry" | | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Type: string
Optional: \{\}
| + +#### vmcp.config.TimeoutConfig + +TimeoutConfig configures timeout settings. + +_Appears in:_ + +- [vmcp.config.OperationalConfig](#vmcpconfigoperationalconfig) + +| Field | Description | Default | Validation | +| ---------------------------------------------------------------------------------------- | ---------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------- | +| `default` _[vmcp.config.Duration](#vmcpconfigduration)_ | Default is the default timeout for backend requests. | 30s | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Type: string
Optional: \{\}
| +| `perWorkload` _object (keys:string, values:[vmcp.config.Duration](#vmcpconfigduration))_ | PerWorkload defines per-workload timeout overrides. | | Optional: \{\}
| + +#### vmcp.config.ToolAnnotationsOverride + +_Underlying type:_ _[vmcp.config.struct{Title *string "json:\"title,omitempty\" yaml:\"title,omitempty\""; ReadOnlyHint *bool "json:\"readOnlyHint,omitempty\" yaml:\"readOnlyHint,omitempty\""; DestructiveHint *bool "json:\"destructiveHint,omitempty\" yaml:\"destructiveHint,omitempty\""; IdempotentHint *bool "json:\"idempotentHint,omitempty\" yaml:\"idempotentHint,omitempty\""; OpenWorldHint *bool "json:\"openWorldHint,omitempty\" yaml:\"openWorldHint,omitempty\""}](#vmcpconfigstruct{title *string "json:\"title,omitempty\" yaml:\"title,omitempty\""; readonlyhint *bool "json:\"readonlyhint,omitempty\" yaml:\"readonlyhint,omitempty\""; destructivehint *bool "json:\"destructivehint,omitempty\" yaml:\"destructivehint,omitempty\""; idempotenthint *bool "json:\"idempotenthint,omitempty\" yaml:\"idempotenthint,omitempty\""; openworldhint \*bool "json:\"openworldhint,omitempty\" yaml:\"openworldhint,omitempty\""})_ + +ToolAnnotationsOverride defines overrides for tool annotation fields. +All fields use pointers so nil means "don't override" while zero values +(empty string, false) mean "explicitly set to this value." + +_Appears in:_ + +- [vmcp.config.ToolOverride](#vmcpconfigtooloverride) + +#### vmcp.config.ToolConfigRef + +ToolConfigRef references an MCPToolConfig resource for tool filtering and renaming. +Only used when running in Kubernetes with the operator. + +_Appears in:_ + +- [vmcp.config.WorkloadToolConfig](#vmcpconfigworkloadtoolconfig) + +| Field | Description | Default | Validation | +| --------------- | --------------------------------------------------------------------- | ------- | --------------------- | +| `name` _string_ | Name is the name of the MCPToolConfig resource in the same namespace. | | Required: \{\}
| + +#### vmcp.config.ToolOverride + +ToolOverride defines tool name, description, and annotation overrides. + +_Appears in:_ + +- [vmcp.config.WorkloadToolConfig](#vmcpconfigworkloadtoolconfig) + +| Field | Description | Default | Validation | +| ----------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `name` _string_ | Name is the new tool name (for renaming). | | Optional: \{\}
| +| `description` _string_ | Description is the new tool description. | | Optional: \{\}
| +| `annotations` _[vmcp.config.ToolAnnotationsOverride](#vmcpconfigtoolannotationsoverride)_ | Annotations overrides specific tool annotation fields.
Only specified fields are overridden; others pass through from the backend. | | Optional: \{\}
| + +#### vmcp.config.WorkflowStepConfig + +WorkflowStepConfig defines a single workflow step. +This matches the proposal's step configuration (lines 180-255). + +_Appears in:_ + +- [vmcp.config.CompositeToolConfig](#vmcpconfigcompositetoolconfig) +- [api.v1alpha1.VirtualMCPCompositeToolDefinitionSpec](#apiv1alpha1virtualmcpcompositetooldefinitionspec) +- [vmcp.config.WorkflowStepConfig](#vmcpconfigworkflowstepconfig) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | --------------------------------------------------------------------------------------------------------- | +| `id` _string_ | ID is the unique identifier for this step. | | Required: \{\}
| +| `type` _string_ | Type is the step type (tool, elicitation, etc.) | tool | Enum: [tool elicitation forEach]
Optional: \{\}
| +| `tool` _string_ | Tool is the tool to call (format: "workload.tool_name")
Only used when Type is "tool" | | Optional: \{\}
| +| `arguments` _[pkg.json.Map](#pkgjsonmap)_ | Arguments is a map of argument values with template expansion support.
Supports Go template syntax with .params and .steps for string values.
Non-string values (integers, booleans, arrays, objects) are passed as-is.
Note: the templating is only supported on the first level of the key-value pairs. | | Type: object
Optional: \{\}
| +| `condition` _string_ | Condition is a template expression that determines if the step should execute | | Optional: \{\}
| +| `dependsOn` _string array_ | DependsOn lists step IDs that must complete before this step | | Optional: \{\}
| +| `onError` _[vmcp.config.StepErrorHandling](#vmcpconfigsteperrorhandling)_ | OnError defines error handling behavior | | Optional: \{\}
| +| `message` _string_ | Message is the elicitation message
Only used when Type is "elicitation" | | Optional: \{\}
| +| `schema` _[pkg.json.Map](#pkgjsonmap)_ | Schema defines the expected response schema for elicitation | | Type: object
Optional: \{\}
| +| `timeout` _[vmcp.config.Duration](#vmcpconfigduration)_ | Timeout is the maximum execution time for this step | | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Type: string
Optional: \{\}
| +| `onDecline` _[vmcp.config.ElicitationResponseConfig](#vmcpconfigelicitationresponseconfig)_ | OnDecline defines the action to take when the user explicitly declines the elicitation
Only used when Type is "elicitation" | | Optional: \{\}
| +| `onCancel` _[vmcp.config.ElicitationResponseConfig](#vmcpconfigelicitationresponseconfig)_ | OnCancel defines the action to take when the user cancels/dismisses the elicitation
Only used when Type is "elicitation" | | Optional: \{\}
| +| `defaultResults` _[pkg.json.Map](#pkgjsonmap)_ | DefaultResults provides fallback output values when this step is skipped
(due to condition evaluating to false) or fails (when onError.action is "continue").
Each key corresponds to an output field name referenced by downstream steps.
Required if the step may be skipped AND downstream steps reference this step's output. | | Schemaless: \{\}
Optional: \{\}
| +| `collection` _string_ | Collection is a Go template expression that resolves to a JSON array or a slice.
Only used when Type is "forEach". | | Optional: \{\}
| +| `itemVar` _string_ | ItemVar is the variable name used to reference the current item in forEach templates.
Defaults to "item" if not specified.
Only used when Type is "forEach". | | Optional: \{\}
| +| `maxParallel` _integer_ | MaxParallel limits the number of concurrent iterations in a forEach step.
Defaults to the DAG executor's maxParallel (10).
Only used when Type is "forEach". | | Optional: \{\}
| +| `maxIterations` _integer_ | MaxIterations limits the number of items that can be iterated over.
Defaults to 100, hard cap at 1000.
Only used when Type is "forEach". | | Optional: \{\}
| +| `step` _[vmcp.config.WorkflowStepConfig](#vmcpconfigworkflowstepconfig)_ | InnerStep defines the step to execute for each item in the collection.
Only used when Type is "forEach". Only tool-type inner steps are supported. | | Type: object
Optional: \{\}
| + +#### vmcp.config.WorkloadToolConfig + +WorkloadToolConfig defines tool filtering and overrides for a specific workload. + +_Appears in:_ + +- [vmcp.config.AggregationConfig](#vmcpconfigaggregationconfig) + +| Field | Description | Default | Validation | +| ---------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `workload` _string_ | Workload is the name of the backend MCPServer workload. | | Required: \{\}
| +| `toolConfigRef` _[vmcp.config.ToolConfigRef](#vmcpconfigtoolconfigref)_ | ToolConfigRef references an MCPToolConfig resource for tool filtering and renaming.
If specified, Filter and Overrides are ignored.
Only used when running in Kubernetes with the operator. | | Optional: \{\}
| +| `filter` _string array_ | Filter is an allow-list of tool names to advertise to MCP clients.
Tools NOT in this list are hidden from clients (not in tools/list response)
but remain available in the routing table for composite tools to use.
This enables selective exposure of backend tools while allowing composite
workflows to orchestrate all backend capabilities.
Only used if ToolConfigRef is not specified. | | Optional: \{\}
| +| `overrides` _object (keys:string, values:[vmcp.config.ToolOverride](#vmcpconfigtooloverride))_ | Overrides is an inline map of tool overrides for renaming and description changes.
Overrides are applied to tools before conflict resolution and affect both
advertising and routing (the overridden name is used everywhere).
Only used if ToolConfigRef is not specified. | | Optional: \{\}
| +| `excludeAll` _boolean_ | ExcludeAll hides all tools from this workload from MCP clients when true.
Hidden tools are NOT advertised in tools/list responses, but they ARE
available in the routing table for composite tools to use.
This enables the use case where you want to hide raw backend tools from
direct client access while exposing curated composite tool workflows. | | Optional: \{\}
| + +## toolhive.stacklok.dev/telemetry + +#### pkg.telemetry.Config + +Config holds the configuration for OpenTelemetry instrumentation. + +_Appears in:_ + +- [vmcp.config.Config](#vmcpconfigconfig) + +| Field | Description | Default | Validation | +| -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `endpoint` _string_ | Endpoint is the OTLP endpoint URL | | Optional: \{\}
| +| `serviceName` _string_ | ServiceName is the service name for telemetry.
When omitted, defaults to the server name (e.g., VirtualMCPServer name). | | Optional: \{\}
| +| `serviceVersion` _string_ | ServiceVersion is the service version for telemetry.
When omitted, defaults to the ToolHive version. | | Optional: \{\}
| +| `tracingEnabled` _boolean_ | TracingEnabled controls whether distributed tracing is enabled.
When false, no tracer provider is created even if an endpoint is configured. | false | Optional: \{\}
| +| `metricsEnabled` _boolean_ | MetricsEnabled controls whether OTLP metrics are enabled.
When false, OTLP metrics are not sent even if an endpoint is configured.
This is independent of EnablePrometheusMetricsPath. | false | Optional: \{\}
| +| `samplingRate` _string_ | SamplingRate is the trace sampling rate (0.0-1.0) as a string.
Only used when TracingEnabled is true.
Example: "0.05" for 5% sampling. | 0.05 | Optional: \{\}
| +| `headers` _object (keys:string, values:string)_ | Headers contains authentication headers for the OTLP endpoint. | | Optional: \{\}
| +| `insecure` _boolean_ | Insecure indicates whether to use HTTP instead of HTTPS for the OTLP endpoint. | false | Optional: \{\}
| +| `enablePrometheusMetricsPath` _boolean_ | EnablePrometheusMetricsPath controls whether to expose Prometheus-style /metrics endpoint.
The metrics are served on the main transport port at /metrics.
This is separate from OTLP metrics which are sent to the Endpoint. | false | Optional: \{\}
| +| `environmentVariables` _string array_ | EnvironmentVariables is a list of environment variable names that should be
included in telemetry spans as attributes. Only variables in this list will
be read from the host machine and included in spans for observability.
Example: ["NODE_ENV", "DEPLOYMENT_ENV", "SERVICE_VERSION"] | | Optional: \{\}
| +| `customAttributes` _object (keys:string, values:string)_ | CustomAttributes contains custom resource attributes to be added to all telemetry signals.
These are parsed from CLI flags (--otel-custom-attributes) or environment variables
(OTEL_RESOURCE_ATTRIBUTES) as key=value pairs. | | Optional: \{\}
| +| `useLegacyAttributes` _boolean_ | UseLegacyAttributes controls whether legacy (pre-MCP OTEL semconv) attribute names
are emitted alongside the new standard attribute names. When true, spans include both
old and new attribute names for backward compatibility with existing dashboards.
Currently defaults to true; this will change to false in a future release. | true | Optional: \{\}
| + +## toolhive.stacklok.dev/v1alpha1 + +### Resource Types + +- [api.v1alpha1.EmbeddingServer](#apiv1alpha1embeddingserver) +- [api.v1alpha1.EmbeddingServerList](#apiv1alpha1embeddingserverlist) +- [api.v1alpha1.MCPExternalAuthConfig](#apiv1alpha1mcpexternalauthconfig) +- [api.v1alpha1.MCPExternalAuthConfigList](#apiv1alpha1mcpexternalauthconfiglist) +- [api.v1alpha1.MCPGroup](#apiv1alpha1mcpgroup) +- [api.v1alpha1.MCPGroupList](#apiv1alpha1mcpgrouplist) +- [api.v1alpha1.MCPRegistry](#apiv1alpha1mcpregistry) +- [api.v1alpha1.MCPRegistryList](#apiv1alpha1mcpregistrylist) +- [api.v1alpha1.MCPRemoteProxy](#apiv1alpha1mcpremoteproxy) +- [api.v1alpha1.MCPRemoteProxyList](#apiv1alpha1mcpremoteproxylist) +- [api.v1alpha1.MCPServer](#apiv1alpha1mcpserver) +- [api.v1alpha1.MCPServerList](#apiv1alpha1mcpserverlist) +- [api.v1alpha1.MCPToolConfig](#apiv1alpha1mcptoolconfig) +- [api.v1alpha1.MCPToolConfigList](#apiv1alpha1mcptoolconfiglist) +- [api.v1alpha1.VirtualMCPCompositeToolDefinition](#apiv1alpha1virtualmcpcompositetooldefinition) +- [api.v1alpha1.VirtualMCPCompositeToolDefinitionList](#apiv1alpha1virtualmcpcompositetooldefinitionlist) +- [api.v1alpha1.VirtualMCPServer](#apiv1alpha1virtualmcpserver) +- [api.v1alpha1.VirtualMCPServerList](#apiv1alpha1virtualmcpserverlist) + +#### api.v1alpha1.APIPhase + +_Underlying type:_ _string_ + +APIPhase represents the API service state + +_Validation:_ + +- Enum: [NotStarted Deploying Ready Unhealthy Error] + +_Appears in:_ + +- [api.v1alpha1.APIStatus](#apiv1alpha1apistatus) + +| Field | Description | +| ------------ | ------------------------------------------------------------------ | +| `NotStarted` | APIPhaseNotStarted means API deployment has not been created
| +| `Deploying` | APIPhaseDeploying means API is being deployed
| +| `Ready` | APIPhaseReady means API is ready to serve requests
| +| `Unhealthy` | APIPhaseUnhealthy means API is deployed but not healthy
| +| `Error` | APIPhaseError means API deployment failed
| + +#### api.v1alpha1.APISource + +APISource defines API source configuration for ToolHive Registry APIs +Phase 1: Supports ToolHive API endpoints (no pagination) +Phase 2: Will add support for upstream MCP Registry API with pagination + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryConfig](#apiv1alpha1mcpregistryconfig) + +| Field | Description | Default | Validation | +| ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | ---------------------------------------------------------------------- | +| `endpoint` _string_ | Endpoint is the base API URL (without path)
The controller will append the appropriate paths:
Phase 1 (ToolHive API):
- /v0/servers - List all servers (single response, no pagination)
- /v0/servers/\{name\} - Get specific server (future)
- /v0/info - Get registry metadata (future)
Example: "http://my-registry-api.default.svc.cluster.local/api" | | MinLength: 1
Pattern: `^https?://.*`
Required: \{\}
| + +#### api.v1alpha1.APIStatus + +APIStatus provides detailed information about the API service + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryStatus](#apiv1alpha1mcpregistrystatus) + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | ------- | --------------------------------------------------------- | +| `phase` _[api.v1alpha1.APIPhase](#apiv1alpha1apiphase)_ | Phase represents the current API service phase | | Enum: [NotStarted Deploying Ready Unhealthy Error]
| +| `message` _string_ | Message provides additional information about the API status | | Optional: \{\}
| +| `endpoint` _string_ | Endpoint is the URL where the API is accessible | | Optional: \{\}
| +| `readySince` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#time-v1-meta)_ | ReadySince is the timestamp when the API became ready | | Optional: \{\}
| + +#### api.v1alpha1.AWSStsConfig + +AWSStsConfig holds configuration for AWS STS authentication with SigV4 request signing. +This configuration exchanges incoming authentication tokens (typically OIDC JWT) for AWS STS +temporary credentials, then signs requests to AWS services using SigV4. + +_Appears in:_ + +- [api.v1alpha1.MCPExternalAuthConfigSpec](#apiv1alpha1mcpexternalauthconfigspec) + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | -------------------------------------------------------------------------------------------------------- | +| `region` _string_ | Region is the AWS region for the STS endpoint and service (e.g., "us-east-1", "eu-west-1") | | MinLength: 1
Pattern: `^[a-z]\{2\}(-[a-z]+)+-\d+$`
Required: \{\}
| +| `service` _string_ | Service is the AWS service name for SigV4 signing
Defaults to "aws-mcp" for AWS MCP Server endpoints | aws-mcp | Optional: \{\}
| +| `fallbackRoleArn` _string_ | FallbackRoleArn is the IAM role ARN to assume when no role mappings match
Used as the default role when RoleMappings is empty or no mapping matches
At least one of FallbackRoleArn or RoleMappings must be configured (enforced by webhook) | | Pattern: `^arn:(aws\|aws-cn\|aws-us-gov):iam::\d\{12\}:role/[\w+=,.@\-_/]+$`
Optional: \{\}
| +| `roleMappings` _[api.v1alpha1.RoleMapping](#apiv1alpha1rolemapping) array_ | RoleMappings defines claim-based role selection rules
Allows mapping JWT claims (e.g., groups, roles) to specific IAM roles
Lower priority values are evaluated first (higher priority) | | Optional: \{\}
| +| `roleClaim` _string_ | RoleClaim is the JWT claim to use for role mapping evaluation
Defaults to "groups" to match common OIDC group claims | groups | Optional: \{\}
| +| `sessionDuration` _integer_ | SessionDuration is the duration in seconds for the STS session
Must be between 900 (15 minutes) and 43200 (12 hours)
Defaults to 3600 (1 hour) if not specified | 3600 | Maximum: 43200
Minimum: 900
Optional: \{\}
| +| `sessionNameClaim` _string_ | SessionNameClaim is the JWT claim to use for role session name
Defaults to "sub" to use the subject claim | sub | Optional: \{\}
| + +#### api.v1alpha1.AuditConfig + +AuditConfig defines audit logging configuration for the MCP server + +_Appears in:_ + +- [api.v1alpha1.MCPRemoteProxySpec](#apiv1alpha1mcpremoteproxyspec) +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) + +| Field | Description | Default | Validation | +| ------------------- | ------------------------------------------------------------------------------------------------------------------ | ------- | --------------------- | +| `enabled` _boolean_ | Enabled controls whether audit logging is enabled
When true, enables audit logging with default configuration | false | Optional: \{\}
| + +#### api.v1alpha1.AuthServerStorageConfig + +AuthServerStorageConfig configures the storage backend for the embedded auth server. + +_Appears in:_ + +- [api.v1alpha1.EmbeddedAuthServerConfig](#apiv1alpha1embeddedauthserverconfig) + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ------- | --------------------------- | +| `type` _[api.v1alpha1.AuthServerStorageType](#apiv1alpha1authserverstoragetype)_ | Type specifies the storage backend type.
Valid values: "memory" (default), "redis". | memory | Enum: [memory redis]
| +| `redis` _[api.v1alpha1.RedisStorageConfig](#apiv1alpha1redisstorageconfig)_ | Redis configures the Redis storage backend.
Required when type is "redis". | | Optional: \{\}
| + +#### api.v1alpha1.AuthServerStorageType + +_Underlying type:_ _string_ + +AuthServerStorageType represents the type of storage backend for the embedded auth server + +_Appears in:_ + +- [api.v1alpha1.AuthServerStorageConfig](#apiv1alpha1authserverstorageconfig) + +| Field | Description | +| -------- | ---------------------------------------------------------------------------- | +| `memory` | AuthServerStorageTypeMemory is the in-memory storage backend (default)
| +| `redis` | AuthServerStorageTypeRedis is the Redis storage backend
| + +#### api.v1alpha1.AuthzConfigRef + +AuthzConfigRef defines a reference to authorization configuration + +_Appears in:_ + +- [api.v1alpha1.IncomingAuthConfig](#apiv1alpha1incomingauthconfig) +- [api.v1alpha1.MCPRemoteProxySpec](#apiv1alpha1mcpremoteproxyspec) +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) + +| Field | Description | Default | Validation | +| ----------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- | --------- | ------------------------------- | +| `type` _string_ | Type is the type of authorization configuration | configMap | Enum: [configMap inline]
| +| `configMap` _[api.v1alpha1.ConfigMapAuthzRef](#apiv1alpha1configmapauthzref)_ | ConfigMap references a ConfigMap containing authorization configuration
Only used when Type is "configMap" | | Optional: \{\}
| +| `inline` _[api.v1alpha1.InlineAuthzConfig](#apiv1alpha1inlineauthzconfig)_ | Inline contains direct authorization configuration
Only used when Type is "inline" | | Optional: \{\}
| + +#### api.v1alpha1.BackendAuthConfig + +BackendAuthConfig defines authentication configuration for a backend MCPServer + +_Appears in:_ + +- [api.v1alpha1.OutgoingAuthConfig](#apiv1alpha1outgoingauthconfig) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- | ------- | ----------------------------------------------------------------------- | +| `type` _string_ | Type defines the authentication type | | Enum: [discovered external_auth_config_ref]
Required: \{\}
| +| `externalAuthConfigRef` _[api.v1alpha1.ExternalAuthConfigRef](#apiv1alpha1externalauthconfigref)_ | ExternalAuthConfigRef references an MCPExternalAuthConfig resource
Only used when Type is "external_auth_config_ref" | | Optional: \{\}
| + +#### api.v1alpha1.BearerTokenConfig + +BearerTokenConfig holds configuration for bearer token authentication. +This allows authenticating to remote MCP servers using bearer tokens stored in Kubernetes Secrets. +For security reasons, only secret references are supported (no plaintext values). + +_Appears in:_ + +- [api.v1alpha1.MCPExternalAuthConfigSpec](#apiv1alpha1mcpexternalauthconfigspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------- | ------- | --------------------- | +| `tokenSecretRef` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref)_ | TokenSecretRef references a Kubernetes Secret containing the bearer token | | Required: \{\}
| + +#### api.v1alpha1.CABundleSource + +CABundleSource defines a source for CA certificate bundles. + +_Appears in:_ + +- [api.v1alpha1.ConfigMapOIDCRef](#apiv1alpha1configmapoidcref) +- [api.v1alpha1.InlineOIDCConfig](#apiv1alpha1inlineoidcconfig) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `configMapRef` _[ConfigMapKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#configmapkeyselector-v1-core)_ | ConfigMapRef references a ConfigMap containing the CA certificate bundle.
If Key is not specified, it defaults to "ca.crt". | | Optional: \{\}
| + +#### api.v1alpha1.ConfigMapAuthzRef + +ConfigMapAuthzRef references a ConfigMap containing authorization configuration + +_Appears in:_ + +- [api.v1alpha1.AuthzConfigRef](#apiv1alpha1authzconfigref) + +| Field | Description | Default | Validation | +| --------------- | ----------------------------------------------------------------------------- | ---------- | --------------------- | +| `name` _string_ | Name is the name of the ConfigMap | | Required: \{\}
| +| `key` _string_ | Key is the key in the ConfigMap that contains the authorization configuration | authz.json | Optional: \{\}
| + +#### api.v1alpha1.ConfigMapOIDCRef + +ConfigMapOIDCRef references a ConfigMap containing OIDC configuration + +_Appears in:_ + +- [api.v1alpha1.OIDCConfigRef](#apiv1alpha1oidcconfigref) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | --------------------- | +| `name` _string_ | Name is the name of the ConfigMap | | Required: \{\}
| +| `key` _string_ | Key is the key in the ConfigMap that contains the OIDC configuration | oidc.json | Optional: \{\}
| +| `caBundleRef` _[api.v1alpha1.CABundleSource](#apiv1alpha1cabundlesource)_ | CABundleRef references a ConfigMap containing the CA certificate bundle.
When specified, ToolHive auto-mounts the ConfigMap and auto-computes ThvCABundlePath.
If the ConfigMap data contains an explicit thvCABundlePath key, it takes precedence. | | Optional: \{\}
| + +#### api.v1alpha1.EmbeddedAuthServerConfig + +EmbeddedAuthServerConfig holds configuration for the embedded OAuth2/OIDC authorization server. +This enables running an authorization server that delegates authentication to upstream IDPs. + +_Appears in:_ + +- [api.v1alpha1.MCPExternalAuthConfigSpec](#apiv1alpha1mcpexternalauthconfigspec) +- [api.v1alpha1.VirtualMCPServerSpec](#apiv1alpha1virtualmcpserverspec) + +| Field | Description | Default | Validation | +| ----------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------ | +| `issuer` _string_ | Issuer is the issuer identifier for this authorization server.
This will be included in the "iss" claim of issued tokens.
Must be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash (per RFC 8414). | | Pattern: `^https?://[^\s?#]+[^/\s?#]$`
Required: \{\}
| +| `authorizationEndpointBaseUrl` _string_ | AuthorizationEndpointBaseURL overrides the base URL used for the authorization_endpoint
in the OAuth discovery document. When set, the discovery document will advertise
`\{authorizationEndpointBaseUrl\}/oauth/authorize` instead of `\{issuer\}/oauth/authorize`.
All other endpoints (token, registration, JWKS) remain derived from the issuer.
This is useful when the browser-facing authorization endpoint needs to be on a
different host than the issuer used for backend-to-backend calls.
Must be a valid HTTPS URL (or HTTP for localhost) without query, fragment, or trailing slash. | | Pattern: `^https?://[^\s?#]+[^/\s?#]$`
Optional: \{\}
| +| `signingKeySecretRefs` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref) array_ | SigningKeySecretRefs references Kubernetes Secrets containing signing keys for JWT operations.
Supports key rotation by allowing multiple keys (oldest keys are used for verification only).
If not specified, an ephemeral signing key will be auto-generated (development only -
JWTs will be invalid after restart). | | MaxItems: 5
Optional: \{\}
| +| `hmacSecretRefs` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref) array_ | HMACSecretRefs references Kubernetes Secrets containing symmetric secrets for signing
authorization codes and refresh tokens (opaque tokens).
Current secret must be at least 32 bytes and cryptographically random.
Supports secret rotation via multiple entries (first is current, rest are for verification).
If not specified, an ephemeral secret will be auto-generated (development only -
auth codes and refresh tokens will be invalid after restart). | | Optional: \{\}
| +| `tokenLifespans` _[api.v1alpha1.TokenLifespanConfig](#apiv1alpha1tokenlifespanconfig)_ | TokenLifespans configures the duration that various tokens are valid.
If not specified, defaults are applied (access: 1h, refresh: 7d, authCode: 10m). | | Optional: \{\}
| +| `upstreamProviders` _[api.v1alpha1.UpstreamProviderConfig](#apiv1alpha1upstreamproviderconfig) array_ | UpstreamProviders configures connections to upstream Identity Providers.
The embedded auth server delegates authentication to these providers.
MCPServer and MCPRemoteProxy support a single upstream; VirtualMCPServer supports multiple. | | MinItems: 1
Required: \{\}
| +| `storage` _[api.v1alpha1.AuthServerStorageConfig](#apiv1alpha1authserverstorageconfig)_ | Storage configures the storage backend for the embedded auth server.
If not specified, defaults to in-memory storage. | | Optional: \{\}
| + +#### api.v1alpha1.EmbeddingResourceOverrides + +EmbeddingResourceOverrides defines overrides for annotations and labels on created resources + +_Appears in:_ + +- [api.v1alpha1.EmbeddingServerSpec](#apiv1alpha1embeddingserverspec) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------ | ------- | --------------------- | +| `statefulSet` _[api.v1alpha1.EmbeddingStatefulSetOverrides](#apiv1alpha1embeddingstatefulsetoverrides)_ | StatefulSet defines overrides for the StatefulSet resource | | Optional: \{\}
| +| `service` _[api.v1alpha1.ResourceMetadataOverrides](#apiv1alpha1resourcemetadataoverrides)_ | Service defines overrides for the Service resource | | Optional: \{\}
| +| `persistentVolumeClaim` _[api.v1alpha1.ResourceMetadataOverrides](#apiv1alpha1resourcemetadataoverrides)_ | PersistentVolumeClaim defines overrides for the PVC resource | | Optional: \{\}
| + +#### api.v1alpha1.EmbeddingServer + +EmbeddingServer is the Schema for the embeddingservers API + +_Appears in:_ + +- [api.v1alpha1.EmbeddingServerList](#apiv1alpha1embeddingserverlist) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `EmbeddingServer` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `spec` _[api.v1alpha1.EmbeddingServerSpec](#apiv1alpha1embeddingserverspec)_ | | | | +| `status` _[api.v1alpha1.EmbeddingServerStatus](#apiv1alpha1embeddingserverstatus)_ | | | | + +#### api.v1alpha1.EmbeddingServerList + +EmbeddingServerList contains a list of EmbeddingServer + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `EmbeddingServerList` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `items` _[api.v1alpha1.EmbeddingServer](#apiv1alpha1embeddingserver) array_ | | | | + +#### api.v1alpha1.EmbeddingServerPhase + +_Underlying type:_ _string_ + +EmbeddingServerPhase is the phase of the EmbeddingServer + +_Validation:_ + +- Enum: [Pending Downloading Running Failed Terminating] + +_Appears in:_ + +- [api.v1alpha1.EmbeddingServerStatus](#apiv1alpha1embeddingserverstatus) + +| Field | Description | +| ------------- | -------------------------------------------------------------------------------- | +| `Pending` | EmbeddingServerPhasePending means the EmbeddingServer is being created
| +| `Downloading` | EmbeddingServerPhaseDownloading means the model is being downloaded
| +| `Running` | EmbeddingServerPhaseRunning means the EmbeddingServer is running and ready
| +| `Failed` | EmbeddingServerPhaseFailed means the EmbeddingServer failed to start
| +| `Terminating` | EmbeddingServerPhaseTerminating means the EmbeddingServer is being deleted
| + +#### api.v1alpha1.EmbeddingServerRef + +EmbeddingServerRef references an existing EmbeddingServer resource by name. +This follows the same pattern as ExternalAuthConfigRef and ToolConfigRef. + +_Appears in:_ + +- [api.v1alpha1.VirtualMCPServerSpec](#apiv1alpha1virtualmcpserverspec) + +| Field | Description | Default | Validation | +| --------------- | ------------------------------------------------ | ------- | --------------------- | +| `name` _string_ | Name is the name of the EmbeddingServer resource | | Required: \{\}
| + +#### api.v1alpha1.EmbeddingServerSpec + +EmbeddingServerSpec defines the desired state of EmbeddingServer + +_Appears in:_ + +- [api.v1alpha1.EmbeddingServer](#apiv1alpha1embeddingserver) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------- | ------------------------------------------------------------- | +| `model` _string_ | Model is the HuggingFace embedding model to use (e.g., "sentence-transformers/all-MiniLM-L6-v2") | BAAI/bge-small-en-v1.5 | Optional: \{\}
| +| `hfTokenSecretRef` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref)_ | HFTokenSecretRef is a reference to a Kubernetes Secret containing the huggingface token.
If provided, the secret value will be provided to the embedding server for authentication with huggingface. | | Optional: \{\}
| +| `image` _string_ | Image is the container image for the embedding inference server.
Images must be from HuggingFace Text Embeddings Inference (https://github.com/huggingface/text-embeddings-inference). | ghcr.io/huggingface/text-embeddings-inference:cpu-latest | Optional: \{\}
| +| `imagePullPolicy` _string_ | ImagePullPolicy defines the pull policy for the container image | IfNotPresent | Enum: [Always Never IfNotPresent]
Optional: \{\}
| +| `port` _integer_ | Port is the port to expose the embedding service on | 8080 | Maximum: 65535
Minimum: 1
| +| `args` _string array_ | Args are additional arguments to pass to the embedding inference server | | Optional: \{\}
| +| `env` _[api.v1alpha1.EnvVar](#apiv1alpha1envvar) array_ | Env are environment variables to set in the container | | Optional: \{\}
| +| `resources` _[api.v1alpha1.ResourceRequirements](#apiv1alpha1resourcerequirements)_ | Resources defines compute resources for the embedding server | | Optional: \{\}
| +| `modelCache` _[api.v1alpha1.ModelCacheConfig](#apiv1alpha1modelcacheconfig)_ | ModelCache configures persistent storage for downloaded models
When enabled, models are cached in a PVC and reused across pod restarts | | Optional: \{\}
| +| `podTemplateSpec` _[RawExtension](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#rawextension-runtime-pkg)_ | PodTemplateSpec allows customizing the pod (node selection, tolerations, etc.)
This field accepts a PodTemplateSpec object as JSON/YAML.
Note that to modify the specific container the embedding server runs in, you must specify
the 'embedding' container name in the PodTemplateSpec. | | Type: object
Optional: \{\}
| +| `resourceOverrides` _[api.v1alpha1.EmbeddingResourceOverrides](#apiv1alpha1embeddingresourceoverrides)_ | ResourceOverrides allows overriding annotations and labels for resources created by the operator | | Optional: \{\}
| +| `replicas` _integer_ | Replicas is the number of embedding server replicas to run | 1 | Minimum: 1
Optional: \{\}
| + +#### api.v1alpha1.EmbeddingServerStatus + +EmbeddingServerStatus defines the observed state of EmbeddingServer + +_Appears in:_ + +- [api.v1alpha1.EmbeddingServer](#apiv1alpha1embeddingserver) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------------------------- | +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#condition-v1-meta) array_ | Conditions represent the latest available observations of the EmbeddingServer's state | | Optional: \{\}
| +| `phase` _[api.v1alpha1.EmbeddingServerPhase](#apiv1alpha1embeddingserverphase)_ | Phase is the current phase of the EmbeddingServer | | Enum: [Pending Downloading Running Failed Terminating]
Optional: \{\}
| +| `message` _string_ | Message provides additional information about the current phase | | Optional: \{\}
| +| `url` _string_ | URL is the URL where the embedding service can be accessed | | Optional: \{\}
| +| `readyReplicas` _integer_ | ReadyReplicas is the number of ready replicas | | Optional: \{\}
| +| `observedGeneration` _integer_ | ObservedGeneration reflects the generation most recently observed by the controller | | Optional: \{\}
| + +#### api.v1alpha1.EmbeddingStatefulSetOverrides + +EmbeddingStatefulSetOverrides defines overrides specific to the embedding statefulset + +_Appears in:_ + +- [api.v1alpha1.EmbeddingResourceOverrides](#apiv1alpha1embeddingresourceoverrides) + +| Field | Description | Default | Validation | +| ---------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------- | ------- | --------------------- | +| `annotations` _object (keys:string, values:string)_ | Annotations to add or override on the resource | | Optional: \{\}
| +| `labels` _object (keys:string, values:string)_ | Labels to add or override on the resource | | Optional: \{\}
| +| `podTemplateMetadataOverrides` _[api.v1alpha1.ResourceMetadataOverrides](#apiv1alpha1resourcemetadataoverrides)_ | PodTemplateMetadataOverrides defines metadata overrides for the pod template | | Optional: \{\}
| + +#### api.v1alpha1.EnvVar + +EnvVar represents an environment variable in a container + +_Appears in:_ + +- [api.v1alpha1.EmbeddingServerSpec](#apiv1alpha1embeddingserverspec) +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) +- [api.v1alpha1.ProxyDeploymentOverrides](#apiv1alpha1proxydeploymentoverrides) + +| Field | Description | Default | Validation | +| ---------------- | --------------------------------- | ------- | --------------------- | +| `name` _string_ | Name of the environment variable | | Required: \{\}
| +| `value` _string_ | Value of the environment variable | | Required: \{\}
| + +#### api.v1alpha1.ExternalAuthConfigRef + +ExternalAuthConfigRef defines a reference to a MCPExternalAuthConfig resource. +The referenced MCPExternalAuthConfig must be in the same namespace as the MCPServer. + +_Appears in:_ + +- [api.v1alpha1.BackendAuthConfig](#apiv1alpha1backendauthconfig) +- [api.v1alpha1.MCPRemoteProxySpec](#apiv1alpha1mcpremoteproxyspec) +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) + +| Field | Description | Default | Validation | +| --------------- | ------------------------------------------------------ | ------- | --------------------- | +| `name` _string_ | Name is the name of the MCPExternalAuthConfig resource | | Required: \{\}
| + +#### api.v1alpha1.ExternalAuthType + +_Underlying type:_ _string_ + +ExternalAuthType represents the type of external authentication + +_Appears in:_ + +- [api.v1alpha1.MCPExternalAuthConfigSpec](#apiv1alpha1mcpexternalauthconfigspec) + +| Field | Description | +| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `tokenExchange` | ExternalAuthTypeTokenExchange is the type for RFC-8693 token exchange
| +| `headerInjection` | ExternalAuthTypeHeaderInjection is the type for custom header injection
| +| `bearerToken` | ExternalAuthTypeBearerToken is the type for bearer token authentication
This allows authenticating to remote MCP servers using bearer tokens stored in Kubernetes Secrets
| +| `unauthenticated` | ExternalAuthTypeUnauthenticated is the type for no authentication
This should only be used for backends on trusted networks (e.g., localhost, VPC)
or when authentication is handled by network-level security
| +| `embeddedAuthServer` | ExternalAuthTypeEmbeddedAuthServer is the type for embedded OAuth2/OIDC authorization server
This enables running an embedded auth server that delegates to upstream IDPs
| +| `awsSts` | ExternalAuthTypeAWSSts is the type for AWS STS authentication
| + +#### api.v1alpha1.GitAuthConfig + +GitAuthConfig defines authentication settings for private Git repositories. +Uses HTTP Basic authentication with username and password/token. +The password is stored in a Kubernetes Secret and mounted as a file +for the registry server to read. + +_Appears in:_ + +- [api.v1alpha1.GitSource](#apiv1alpha1gitsource) + +| Field | Description | Default | Validation | +| ----------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------- | +| `username` _string_ | Username is the Git username for HTTP Basic authentication.
For GitHub/GitLab token-based auth, this is typically the literal string "git"
or the token itself depending on the provider. | | MinLength: 1
Required: \{\}
| +| `passwordSecretRef` _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#secretkeyselector-v1-core)_ | PasswordSecretRef references a Kubernetes Secret containing the password or token
for Git authentication. The secret value will be mounted as a file and its path
passed to the registry server via the git.auth.passwordFile configuration.
Example secret:
apiVersion: v1
kind: Secret
metadata:
name: git-credentials
stringData:
token:
Then reference it as:
passwordSecretRef:
name: git-credentials
key: token | | Required: \{\}
| + +#### api.v1alpha1.GitSource + +GitSource defines Git repository source configuration + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryConfig](#apiv1alpha1mcpregistryconfig) + +| Field | Description | Default | Validation | +| ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | -------------------------------------------------------------------------------------------------------- | +| `repository` _string_ | Repository is the Git repository URL (HTTP/HTTPS/SSH) | | MinLength: 1
Pattern: `^(file:///\|https?://\|git@\|ssh://\|git://).*`
Required: \{\}
| +| `branch` _string_ | Branch is the Git branch to use (mutually exclusive with Tag and Commit) | | MinLength: 1
Optional: \{\}
| +| `tag` _string_ | Tag is the Git tag to use (mutually exclusive with Branch and Commit) | | MinLength: 1
Optional: \{\}
| +| `commit` _string_ | Commit is the Git commit SHA to use (mutually exclusive with Branch and Tag) | | MinLength: 1
Optional: \{\}
| +| `path` _string_ | Path is the path to the registry file within the repository | registry.json | Pattern: `^.*\.json$`
Optional: \{\}
| +| `auth` _[api.v1alpha1.GitAuthConfig](#apiv1alpha1gitauthconfig)_ | Auth defines optional authentication for private Git repositories.
When specified, enables HTTP Basic authentication using the provided
username and password/token from a Kubernetes Secret. | | Optional: \{\}
| + +#### api.v1alpha1.HeaderForwardConfig + +HeaderForwardConfig defines header forward configuration for remote servers. + +_Appears in:_ + +- [api.v1alpha1.MCPRemoteProxySpec](#apiv1alpha1mcpremoteproxyspec) + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | --------------------- | +| `addPlaintextHeaders` _object (keys:string, values:string)_ | AddPlaintextHeaders is a map of header names to literal values to inject into requests.
WARNING: Values are stored in plaintext and visible via kubectl commands.
Use addHeadersFromSecret for sensitive data like API keys or tokens. | | Optional: \{\}
| +| `addHeadersFromSecret` _[api.v1alpha1.HeaderFromSecret](#apiv1alpha1headerfromsecret) array_ | AddHeadersFromSecret references Kubernetes Secrets for sensitive header values. | | Optional: \{\}
| + +#### api.v1alpha1.HeaderFromSecret + +HeaderFromSecret defines a header whose value comes from a Kubernetes Secret. + +_Appears in:_ + +- [api.v1alpha1.HeaderForwardConfig](#apiv1alpha1headerforwardconfig) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------ | ------- | ------------------------------------------------------------- | +| `headerName` _string_ | HeaderName is the HTTP header name (e.g., "X-API-Key") | | MaxLength: 255
MinLength: 1
Required: \{\}
| +| `valueSecretRef` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref)_ | ValueSecretRef references the Secret and key containing the header value | | Required: \{\}
| + +#### api.v1alpha1.HeaderInjectionConfig + +HeaderInjectionConfig holds configuration for custom HTTP header injection authentication. +This allows injecting a secret-based header value into requests to backend MCP servers. +For security reasons, only secret references are supported (no plaintext values). + +_Appears in:_ + +- [api.v1alpha1.MCPExternalAuthConfigSpec](#apiv1alpha1mcpexternalauthconfigspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------- | ------- | ---------------------------------------- | +| `headerName` _string_ | HeaderName is the name of the HTTP header to inject | | MinLength: 1
Required: \{\}
| +| `valueSecretRef` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref)_ | ValueSecretRef references a Kubernetes Secret containing the header value | | Required: \{\}
| + +#### api.v1alpha1.IncomingAuthConfig + +IncomingAuthConfig configures authentication for clients connecting to the Virtual MCP server + +_Appears in:_ + +- [api.v1alpha1.VirtualMCPServerSpec](#apiv1alpha1virtualmcpserverspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ------- | -------------------------------------------------- | +| `type` _string_ | Type defines the authentication type: anonymous or oidc
When no authentication is required, explicitly set this to "anonymous" | | Enum: [anonymous oidc]
Required: \{\}
| +| `oidcConfig` _[api.v1alpha1.OIDCConfigRef](#apiv1alpha1oidcconfigref)_ | OIDCConfig defines OIDC authentication configuration
Reuses MCPServer OIDC patterns | | Optional: \{\}
| +| `authzConfig` _[api.v1alpha1.AuthzConfigRef](#apiv1alpha1authzconfigref)_ | AuthzConfig defines authorization policy configuration
Reuses MCPServer authz patterns | | Optional: \{\}
| + +#### api.v1alpha1.InlineAuthzConfig + +InlineAuthzConfig contains direct authorization configuration + +_Appears in:_ + +- [api.v1alpha1.AuthzConfigRef](#apiv1alpha1authzconfigref) + +| Field | Description | Default | Validation | +| ------------------------- | --------------------------------------------------------- | ------- | --------------------------------------- | +| `policies` _string array_ | Policies is a list of Cedar policy strings | | MinItems: 1
Required: \{\}
| +| `entitiesJson` _string_ | EntitiesJSON is a JSON string representing Cedar entities | [] | Optional: \{\}
| + +#### api.v1alpha1.InlineOIDCConfig + +InlineOIDCConfig contains direct OIDC configuration + +_Appears in:_ + +- [api.v1alpha1.OIDCConfigRef](#apiv1alpha1oidcconfigref) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `issuer` _string_ | Issuer is the OIDC issuer URL | | Required: \{\}
| +| `audience` _string_ | Audience is the expected audience for the token | | Optional: \{\}
| +| `jwksUrl` _string_ | JWKSURL is the URL to fetch the JWKS from | | Optional: \{\}
| +| `introspectionUrl` _string_ | IntrospectionURL is the URL for token introspection endpoint | | Optional: \{\}
| +| `clientId` _string_ | ClientID is the OIDC client ID | | Optional: \{\}
| +| `clientSecret` _string_ | ClientSecret is the client secret for introspection (optional)
Deprecated: Use ClientSecretRef instead for better security | | Optional: \{\}
| +| `clientSecretRef` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref)_ | ClientSecretRef is a reference to a Kubernetes Secret containing the client secret
If both ClientSecret and ClientSecretRef are provided, ClientSecretRef takes precedence | | Optional: \{\}
| +| `thvCABundlePath` _string_ | ThvCABundlePath is the path to CA certificate bundle file for HTTPS requests.
Deprecated: Use CABundleRef instead. ThvCABundlePath requires the CA bundle to
already exist in the proxy runner container (e.g., Kubernetes service account CA at
/var/run/secrets/kubernetes.io/serviceaccount/ca.crt). For custom CA certificates,
use CABundleRef which automatically mounts the ConfigMap and computes the path.
This field will be removed when the API graduates to v1beta1. | | Optional: \{\}
| +| `caBundleRef` _[api.v1alpha1.CABundleSource](#apiv1alpha1cabundlesource)_ | CABundleRef references a ConfigMap containing the CA certificate bundle.
When specified, ToolHive auto-mounts the ConfigMap and auto-computes ThvCABundlePath.
If ThvCABundlePath is explicitly set, it takes precedence over CABundleRef. | | Optional: \{\}
| +| `jwksAuthTokenPath` _string_ | JWKSAuthTokenPath is the path to file containing bearer token for JWKS/OIDC requests
The file must be mounted into the pod (e.g., via Secret volume) | | Optional: \{\}
| +| `jwksAllowPrivateIP` _boolean_ | JWKSAllowPrivateIP allows JWKS/OIDC endpoints on private IP addresses
Use with caution - only enable for trusted internal IDPs | false | Optional: \{\}
| +| `protectedResourceAllowPrivateIP` _boolean_ | ProtectedResourceAllowPrivateIP allows protected resource endpoint on private IP addresses
Use with caution - only enable for trusted internal IDPs or testing | false | Optional: \{\}
| +| `insecureAllowHTTP` _boolean_ | InsecureAllowHTTP allows HTTP (non-HTTPS) OIDC issuers for development/testing
WARNING: This is insecure and should NEVER be used in production
Only enable for local development, testing, or trusted internal networks | false | Optional: \{\}
| +| `scopes` _string array_ | Scopes is the list of OAuth scopes to advertise in the well-known endpoint (RFC 9728)
If empty, defaults to ["openid"] | | Optional: \{\}
| + +#### api.v1alpha1.KubernetesOIDCConfig + +KubernetesOIDCConfig configures OIDC for Kubernetes service account token validation + +_Appears in:_ + +- [api.v1alpha1.OIDCConfigRef](#apiv1alpha1oidcconfigref) + +| Field | Description | Default | Validation | +| --------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ | --------------------- | +| `serviceAccount` _string_ | ServiceAccount is the name of the service account to validate tokens for
If empty, uses the pod's service account | | Optional: \{\}
| +| `namespace` _string_ | Namespace is the namespace of the service account
If empty, uses the MCPServer's namespace | | Optional: \{\}
| +| `audience` _string_ | Audience is the expected audience for the token | toolhive | Optional: \{\}
| +| `issuer` _string_ | Issuer is the OIDC issuer URL | https://kubernetes.default.svc | Optional: \{\}
| +| `jwksUrl` _string_ | JWKSURL is the URL to fetch the JWKS from
If empty, OIDC discovery will be used to automatically determine the JWKS URL | | Optional: \{\}
| +| `introspectionUrl` _string_ | IntrospectionURL is the URL for token introspection endpoint
If empty, OIDC discovery will be used to automatically determine the introspection URL | | Optional: \{\}
| +| `useClusterAuth` _boolean_ | UseClusterAuth enables using the Kubernetes cluster's CA bundle and service account token
When true, uses /var/run/secrets/kubernetes.io/serviceaccount/ca.crt for TLS verification
and /var/run/secrets/kubernetes.io/serviceaccount/token for bearer token authentication
Defaults to true if not specified | | Optional: \{\}
| + +#### api.v1alpha1.MCPExternalAuthConfig + +MCPExternalAuthConfig is the Schema for the mcpexternalauthconfigs API. +MCPExternalAuthConfig resources are namespace-scoped and can only be referenced by +MCPServer resources within the same namespace. Cross-namespace references +are not supported for security and isolation reasons. + +_Appears in:_ + +- [api.v1alpha1.MCPExternalAuthConfigList](#apiv1alpha1mcpexternalauthconfiglist) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `MCPExternalAuthConfig` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `spec` _[api.v1alpha1.MCPExternalAuthConfigSpec](#apiv1alpha1mcpexternalauthconfigspec)_ | | | | +| `status` _[api.v1alpha1.MCPExternalAuthConfigStatus](#apiv1alpha1mcpexternalauthconfigstatus)_ | | | | + +#### api.v1alpha1.MCPExternalAuthConfigList + +MCPExternalAuthConfigList contains a list of MCPExternalAuthConfig + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `MCPExternalAuthConfigList` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `items` _[api.v1alpha1.MCPExternalAuthConfig](#apiv1alpha1mcpexternalauthconfig) array_ | | | | + +#### api.v1alpha1.MCPExternalAuthConfigSpec + +MCPExternalAuthConfigSpec defines the desired state of MCPExternalAuthConfig. +MCPExternalAuthConfig resources are namespace-scoped and can only be referenced by +MCPServer resources in the same namespace. + +_Appears in:_ + +- [api.v1alpha1.MCPExternalAuthConfig](#apiv1alpha1mcpexternalauthconfig) + +| Field | Description | Default | Validation | +| ---------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------- | ----------------------------------------------------------------------------------------------------------------------- | +| `type` _[api.v1alpha1.ExternalAuthType](#apiv1alpha1externalauthtype)_ | Type is the type of external authentication to configure | | Enum: [tokenExchange headerInjection bearerToken unauthenticated embeddedAuthServer awsSts]
Required: \{\}
| +| `tokenExchange` _[api.v1alpha1.TokenExchangeConfig](#apiv1alpha1tokenexchangeconfig)_ | TokenExchange configures RFC-8693 OAuth 2.0 Token Exchange
Only used when Type is "tokenExchange" | | Optional: \{\}
| +| `headerInjection` _[api.v1alpha1.HeaderInjectionConfig](#apiv1alpha1headerinjectionconfig)_ | HeaderInjection configures custom HTTP header injection
Only used when Type is "headerInjection" | | Optional: \{\}
| +| `bearerToken` _[api.v1alpha1.BearerTokenConfig](#apiv1alpha1bearertokenconfig)_ | BearerToken configures bearer token authentication
Only used when Type is "bearerToken" | | Optional: \{\}
| +| `embeddedAuthServer` _[api.v1alpha1.EmbeddedAuthServerConfig](#apiv1alpha1embeddedauthserverconfig)_ | EmbeddedAuthServer configures an embedded OAuth2/OIDC authorization server
Only used when Type is "embeddedAuthServer" | | Optional: \{\}
| +| `awsSts` _[api.v1alpha1.AWSStsConfig](#apiv1alpha1awsstsconfig)_ | AWSSts configures AWS STS authentication with SigV4 request signing
Only used when Type is "awsSts" | | Optional: \{\}
| + +#### api.v1alpha1.MCPExternalAuthConfigStatus + +MCPExternalAuthConfigStatus defines the observed state of MCPExternalAuthConfig + +_Appears in:_ + +- [api.v1alpha1.MCPExternalAuthConfig](#apiv1alpha1mcpexternalauthconfig) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#condition-v1-meta) array_ | Conditions represent the latest available observations of the MCPExternalAuthConfig's state | | Optional: \{\}
| +| `observedGeneration` _integer_ | ObservedGeneration is the most recent generation observed for this MCPExternalAuthConfig.
It corresponds to the MCPExternalAuthConfig's generation, which is updated on mutation by the API Server. | | Optional: \{\}
| +| `configHash` _string_ | ConfigHash is a hash of the current configuration for change detection | | Optional: \{\}
| +| `referencingServers` _string array_ | ReferencingServers is a list of MCPServer resources that reference this MCPExternalAuthConfig
This helps track which servers need to be reconciled when this config changes | | Optional: \{\}
| + +#### api.v1alpha1.MCPGroup + +MCPGroup is the Schema for the mcpgroups API + +_Appears in:_ + +- [api.v1alpha1.MCPGroupList](#apiv1alpha1mcpgrouplist) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `MCPGroup` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `spec` _[api.v1alpha1.MCPGroupSpec](#apiv1alpha1mcpgroupspec)_ | | | | +| `status` _[api.v1alpha1.MCPGroupStatus](#apiv1alpha1mcpgroupstatus)_ | | | | + +#### api.v1alpha1.MCPGroupList + +MCPGroupList contains a list of MCPGroup + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `MCPGroupList` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `items` _[api.v1alpha1.MCPGroup](#apiv1alpha1mcpgroup) array_ | | | | + +#### api.v1alpha1.MCPGroupPhase + +_Underlying type:_ _string_ + +MCPGroupPhase represents the lifecycle phase of an MCPGroup + +_Validation:_ + +- Enum: [Ready Pending Failed] + +_Appears in:_ + +- [api.v1alpha1.MCPGroupStatus](#apiv1alpha1mcpgroupstatus) + +| Field | Description | +| --------- | ------------------------------------------------------------ | +| `Ready` | MCPGroupPhaseReady indicates the MCPGroup is ready
| +| `Pending` | MCPGroupPhasePending indicates the MCPGroup is pending
| +| `Failed` | MCPGroupPhaseFailed indicates the MCPGroup has failed
| + +#### api.v1alpha1.MCPGroupSpec + +MCPGroupSpec defines the desired state of MCPGroup + +_Appears in:_ + +- [api.v1alpha1.MCPGroup](#apiv1alpha1mcpgroup) + +| Field | Description | Default | Validation | +| ---------------------- | ------------------------------------------- | ------- | --------------------- | +| `description` _string_ | Description provides human-readable context | | Optional: \{\}
| + +#### api.v1alpha1.MCPGroupStatus + +MCPGroupStatus defines observed state + +_Appears in:_ + +- [api.v1alpha1.MCPGroup](#apiv1alpha1mcpgroup) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------ | ------- | -------------------------------------------------------- | +| `phase` _[api.v1alpha1.MCPGroupPhase](#apiv1alpha1mcpgroupphase)_ | Phase indicates current state | Pending | Enum: [Ready Pending Failed]
Optional: \{\}
| +| `servers` _string array_ | Servers lists MCPServer names in this group | | Optional: \{\}
| +| `serverCount` _integer_ | ServerCount is the number of MCPServers | | Optional: \{\}
| +| `remoteProxies` _string array_ | RemoteProxies lists MCPRemoteProxy names in this group | | Optional: \{\}
| +| `remoteProxyCount` _integer_ | RemoteProxyCount is the number of MCPRemoteProxies | | Optional: \{\}
| +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#condition-v1-meta) array_ | Conditions represent observations | | Optional: \{\}
| + +#### api.v1alpha1.MCPRegistry + +MCPRegistry is the Schema for the mcpregistries API + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryList](#apiv1alpha1mcpregistrylist) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `MCPRegistry` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `spec` _[api.v1alpha1.MCPRegistrySpec](#apiv1alpha1mcpregistryspec)_ | | | | +| `status` _[api.v1alpha1.MCPRegistryStatus](#apiv1alpha1mcpregistrystatus)_ | | | | + +#### api.v1alpha1.MCPRegistryAuthConfig + +MCPRegistryAuthConfig defines authentication configuration for the registry API server. + +_Appears in:_ + +- [api.v1alpha1.MCPRegistrySpec](#apiv1alpha1mcpregistryspec) + +| Field | Description | Default | Validation | +| ----------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------- | --------------------------------------------------- | +| `mode` _[api.v1alpha1.MCPRegistryAuthMode](#apiv1alpha1mcpregistryauthmode)_ | Mode specifies the authentication mode (anonymous or oauth)
Defaults to "anonymous" if not specified.
Use "oauth" to enable OAuth/OIDC authentication. | anonymous | Enum: [anonymous oauth]
Optional: \{\}
| +| `oauth` _[api.v1alpha1.MCPRegistryOAuthConfig](#apiv1alpha1mcpregistryoauthconfig)_ | OAuth defines OAuth/OIDC specific authentication settings
Only used when Mode is "oauth" | | Optional: \{\}
| + +#### api.v1alpha1.MCPRegistryAuthMode + +_Underlying type:_ _string_ + +MCPRegistryAuthMode represents the authentication mode for the registry API server + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryAuthConfig](#apiv1alpha1mcpregistryauthconfig) + +| Field | Description | +| ----------- | ---------------------------------------------------------------- | +| `anonymous` | MCPRegistryAuthModeAnonymous allows unauthenticated access
| +| `oauth` | MCPRegistryAuthModeOAuth enables OAuth/OIDC authentication
| + +#### api.v1alpha1.MCPRegistryConfig + +MCPRegistryConfig defines the configuration for a registry data source + +_Appears in:_ + +- [api.v1alpha1.MCPRegistrySpec](#apiv1alpha1mcpregistryspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | ---------------------------------------- | +| `name` _string_ | Name is a unique identifier for this registry configuration within the MCPRegistry | | MinLength: 1
Required: \{\}
| +| `format` _string_ | Format is the data format (toolhive, upstream) | toolhive | Enum: [toolhive upstream]
| +| `configMapRef` _[ConfigMapKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#configmapkeyselector-v1-core)_ | ConfigMapRef defines the ConfigMap source configuration
Mutually exclusive with Git, API, and PVCRef | | Optional: \{\}
| +| `git` _[api.v1alpha1.GitSource](#apiv1alpha1gitsource)_ | Git defines the Git repository source configuration
Mutually exclusive with ConfigMapRef, API, and PVCRef | | Optional: \{\}
| +| `api` _[api.v1alpha1.APISource](#apiv1alpha1apisource)_ | API defines the API source configuration
Mutually exclusive with ConfigMapRef, Git, and PVCRef | | Optional: \{\}
| +| `pvcRef` _[api.v1alpha1.PVCSource](#apiv1alpha1pvcsource)_ | PVCRef defines the PersistentVolumeClaim source configuration
Mutually exclusive with ConfigMapRef, Git, and API | | Optional: \{\}
| +| `syncPolicy` _[api.v1alpha1.SyncPolicy](#apiv1alpha1syncpolicy)_ | SyncPolicy defines the automatic synchronization behavior for this registry.
If specified, enables automatic synchronization at the given interval.
Manual synchronization is always supported via annotation-based triggers
regardless of this setting. | | Optional: \{\}
| +| `filter` _[api.v1alpha1.RegistryFilter](#apiv1alpha1registryfilter)_ | Filter defines include/exclude patterns for registry content | | Optional: \{\}
| + +#### api.v1alpha1.MCPRegistryDatabaseConfig + +MCPRegistryDatabaseConfig defines PostgreSQL database configuration for the registry API server. +Uses a two-user security model: separate users for operations and migrations. + +_Appears in:_ + +- [api.v1alpha1.MCPRegistrySpec](#apiv1alpha1mcpregistryspec) + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | -------------------------------------------------------------------------------------- | +| `host` _string_ | Host is the database server hostname | postgres | Optional: \{\}
| +| `port` _integer_ | Port is the database server port | 5432 | Maximum: 65535
Minimum: 1
Optional: \{\}
| +| `user` _string_ | User is the application user (limited privileges: SELECT, INSERT, UPDATE, DELETE)
Credentials should be provided via pgpass file or environment variables | db_app | Optional: \{\}
| +| `migrationUser` _string_ | MigrationUser is the migration user (elevated privileges: CREATE, ALTER, DROP)
Used for running database schema migrations
Credentials should be provided via pgpass file or environment variables | db_migrator | Optional: \{\}
| +| `database` _string_ | Database is the database name | registry | Optional: \{\}
| +| `sslMode` _string_ | SSLMode is the SSL mode for the connection
Valid values: disable, allow, prefer, require, verify-ca, verify-full | prefer | Enum: [disable allow prefer require verify-ca verify-full]
Optional: \{\}
| +| `maxOpenConns` _integer_ | MaxOpenConns is the maximum number of open connections to the database | 10 | Minimum: 1
Optional: \{\}
| +| `maxIdleConns` _integer_ | MaxIdleConns is the maximum number of idle connections in the pool | 2 | Minimum: 0
Optional: \{\}
| +| `connMaxLifetime` _string_ | ConnMaxLifetime is the maximum amount of time a connection may be reused (Go duration format)
Examples: "30m", "1h", "24h" | 30m | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Optional: \{\}
| +| `dbAppUserPasswordSecretRef` _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#secretkeyselector-v1-core)_ | DBAppUserPasswordSecretRef references a Kubernetes Secret containing the password for the application database user.
The operator will use this password along with DBMigrationUserPasswordSecretRef to generate a pgpass file
that is mounted to the registry API container. | | Required: \{\}
| +| `dbMigrationUserPasswordSecretRef` _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#secretkeyselector-v1-core)_ | DBMigrationUserPasswordSecretRef references a Kubernetes Secret containing the password for the migration database user.
The operator will use this password along with DBAppUserPasswordSecretRef to generate a pgpass file
that is mounted to the registry API container. | | Required: \{\}
| + +#### api.v1alpha1.MCPRegistryList + +MCPRegistryList contains a list of MCPRegistry + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `MCPRegistryList` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `items` _[api.v1alpha1.MCPRegistry](#apiv1alpha1mcpregistry) array_ | | | | + +#### api.v1alpha1.MCPRegistryOAuthConfig + +MCPRegistryOAuthConfig defines OAuth/OIDC specific authentication settings + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryAuthConfig](#apiv1alpha1mcpregistryauthconfig) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | --------------------------------------- | +| `resourceUrl` _string_ | ResourceURL is the URL identifying this protected resource (RFC 9728)
Used in the /.well-known/oauth-protected-resource endpoint | | Optional: \{\}
| +| `providers` _[api.v1alpha1.MCPRegistryOAuthProviderConfig](#apiv1alpha1mcpregistryoauthproviderconfig) array_ | Providers defines the OAuth/OIDC providers for authentication
Multiple providers can be configured (e.g., Kubernetes + external IDP) | | MinItems: 1
Optional: \{\}
| +| `scopesSupported` _string array_ | ScopesSupported defines the OAuth scopes supported by this resource (RFC 9728)
Defaults to ["mcp-registry:read", "mcp-registry:write"] if not specified | | Optional: \{\}
| +| `realm` _string_ | Realm is the protection space identifier for WWW-Authenticate header (RFC 7235)
Defaults to "mcp-registry" if not specified | | Optional: \{\}
| + +#### api.v1alpha1.MCPRegistryOAuthProviderConfig + +MCPRegistryOAuthProviderConfig defines configuration for an OAuth/OIDC provider + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryOAuthConfig](#apiv1alpha1mcpregistryoauthconfig) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------------- | +| `name` _string_ | Name is a unique identifier for this provider (e.g., "kubernetes", "keycloak") | | MinLength: 1
Required: \{\}
| +| `issuerUrl` _string_ | IssuerURL is the OIDC issuer URL (e.g., https://accounts.google.com)
The JWKS URL will be discovered automatically from .well-known/openid-configuration
unless JwksUrl is explicitly specified | | MinLength: 1
Pattern: `^https?://.*`
Required: \{\}
| +| `jwksUrl` _string_ | JwksUrl is the URL to fetch the JSON Web Key Set (JWKS) from
If specified, OIDC discovery is skipped and this URL is used directly
Example: https://kubernetes.default.svc/openid/v1/jwks | | Pattern: `^https?://.*`
Optional: \{\}
| +| `audience` _string_ | Audience is the expected audience claim in the token (REQUIRED)
Per RFC 6749 Section 4.1.3, tokens must be validated against expected audience
For Kubernetes, this is typically the API server URL | | MinLength: 1
Required: \{\}
| +| `clientId` _string_ | ClientID is the OAuth client ID for token introspection (optional) | | Optional: \{\}
| +| `clientSecretRef` _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#secretkeyselector-v1-core)_ | ClientSecretRef is a reference to a Secret containing the client secret
The secret should have a key "clientSecret" containing the secret value | | Optional: \{\}
| +| `caCertRef` _[ConfigMapKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#configmapkeyselector-v1-core)_ | CACertRef is a reference to a ConfigMap containing the CA certificate bundle
for verifying the provider's TLS certificate.
Required for Kubernetes in-cluster authentication or self-signed certificates | | Optional: \{\}
| +| `caCertPath` _string_ | CaCertPath is the path to the CA certificate bundle for verifying the provider's TLS certificate.
Required for Kubernetes in-cluster authentication or self-signed certificates | | Optional: \{\}
| +| `authTokenRef` _[SecretKeySelector](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#secretkeyselector-v1-core)_ | AuthTokenRef is a reference to a Secret containing a bearer token for authenticating
to OIDC/JWKS endpoints. Useful when the OIDC discovery or JWKS endpoint requires authentication.
Example: ServiceAccount token for Kubernetes API server | | Optional: \{\}
| +| `authTokenFile` _string_ | AuthTokenFile is the path to a file containing a bearer token for authenticating to OIDC/JWKS endpoints.
Useful when the OIDC discovery or JWKS endpoint requires authentication.
Example: /var/run/secrets/kubernetes.io/serviceaccount/token | | Optional: \{\}
| +| `introspectionUrl` _string_ | IntrospectionURL is the OAuth 2.0 Token Introspection endpoint (RFC 7662)
Used for validating opaque (non-JWT) tokens
If not specified, only JWT tokens can be validated via JWKS | | Pattern: `^https?://.*`
Optional: \{\}
| +| `allowPrivateIP` _boolean_ | AllowPrivateIP allows JWKS/OIDC endpoints on private IP addresses
Required when the OAuth provider (e.g., Kubernetes API server) is running on a private network
Example: Set to true when using https://kubernetes.default.svc as the issuer URL | false | Optional: \{\}
| + +#### api.v1alpha1.MCPRegistryPhase + +_Underlying type:_ _string_ + +MCPRegistryPhase represents the phase of the MCPRegistry + +_Validation:_ + +- Enum: [Pending Ready Failed Syncing Terminating] + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryStatus](#apiv1alpha1mcpregistrystatus) + +| Field | Description | +| ------------- | ----------------------------------------------------------------------------- | +| `Pending` | MCPRegistryPhasePending means the MCPRegistry is being initialized
| +| `Ready` | MCPRegistryPhaseReady means the MCPRegistry is ready and operational
| +| `Failed` | MCPRegistryPhaseFailed means the MCPRegistry has failed
| +| `Syncing` | MCPRegistryPhaseSyncing means the MCPRegistry is currently syncing data
| +| `Terminating` | MCPRegistryPhaseTerminating means the MCPRegistry is being deleted
| + +#### api.v1alpha1.MCPRegistrySpec + +MCPRegistrySpec defines the desired state of MCPRegistry + +_Appears in:_ + +- [api.v1alpha1.MCPRegistry](#apiv1alpha1mcpregistry) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------- | +| `displayName` _string_ | DisplayName is a human-readable name for the registry | | Optional: \{\}
| +| `registries` _[api.v1alpha1.MCPRegistryConfig](#apiv1alpha1mcpregistryconfig) array_ | Registries defines the configuration for the registry data sources | | MinItems: 1
Required: \{\}
| +| `enforceServers` _boolean_ | EnforceServers indicates whether MCPServers in this namespace must have their images
present in at least one registry in the namespace. When any registry in the namespace
has this field set to true, enforcement is enabled for the entire namespace.
MCPServers with images not found in any registry will be rejected.
When false (default), MCPServers can be deployed regardless of registry presence. | false | Optional: \{\}
| +| `podTemplateSpec` _[RawExtension](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#rawextension-runtime-pkg)_ | PodTemplateSpec defines the pod template to use for the registry API server
This allows for customizing the pod configuration beyond what is provided by the other fields.
Note that to modify the specific container the registry API server runs in, you must specify
the `registry-api` container name in the PodTemplateSpec.
This field accepts a PodTemplateSpec object as JSON/YAML. | | Type: object
Optional: \{\}
| +| `databaseConfig` _[api.v1alpha1.MCPRegistryDatabaseConfig](#apiv1alpha1mcpregistrydatabaseconfig)_ | DatabaseConfig defines the PostgreSQL database configuration for the registry API server.
If not specified, defaults will be used:
- Host: "postgres"
- Port: 5432
- User: "db_app"
- MigrationUser: "db_migrator"
- Database: "registry"
- SSLMode: "prefer"
- MaxOpenConns: 10
- MaxIdleConns: 2
- ConnMaxLifetime: "30m" | | Optional: \{\}
| +| `authConfig` _[api.v1alpha1.MCPRegistryAuthConfig](#apiv1alpha1mcpregistryauthconfig)_ | AuthConfig defines the authentication configuration for the registry API server.
If not specified, defaults to anonymous authentication. | | Optional: \{\}
| + +#### api.v1alpha1.MCPRegistryStatus + +MCPRegistryStatus defines the observed state of MCPRegistry + +_Appears in:_ + +- [api.v1alpha1.MCPRegistry](#apiv1alpha1mcpregistry) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------------------------------------------- | +| `phase` _[api.v1alpha1.MCPRegistryPhase](#apiv1alpha1mcpregistryphase)_ | Phase represents the current overall phase of the MCPRegistry
Derived from sync and API status | | Enum: [Pending Ready Failed Syncing Terminating]
Optional: \{\}
| +| `message` _string_ | Message provides additional information about the current phase | | Optional: \{\}
| +| `syncStatus` _[api.v1alpha1.SyncStatus](#apiv1alpha1syncstatus)_ | SyncStatus provides detailed information about data synchronization | | Optional: \{\}
| +| `apiStatus` _[api.v1alpha1.APIStatus](#apiv1alpha1apistatus)_ | APIStatus provides detailed information about the API service | | Optional: \{\}
| +| `lastAppliedFilterHash` _string_ | LastAppliedFilterHash is the hash of the last applied filter | | Optional: \{\}
| +| `storageRef` _[api.v1alpha1.StorageReference](#apiv1alpha1storagereference)_ | StorageRef is a reference to the internal storage location | | Optional: \{\}
| +| `lastManualSyncTrigger` _string_ | LastManualSyncTrigger tracks the last processed manual sync annotation value
Used to detect new manual sync requests via toolhive.stacklok.dev/sync-trigger annotation | | Optional: \{\}
| +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#condition-v1-meta) array_ | Conditions represent the latest available observations of the MCPRegistry's state | | Optional: \{\}
| + +#### api.v1alpha1.MCPRemoteProxy + +MCPRemoteProxy is the Schema for the mcpremoteproxies API +It enables proxying remote MCP servers with authentication, authorization, audit logging, and tool filtering + +_Appears in:_ + +- [api.v1alpha1.MCPRemoteProxyList](#apiv1alpha1mcpremoteproxylist) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `MCPRemoteProxy` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `spec` _[api.v1alpha1.MCPRemoteProxySpec](#apiv1alpha1mcpremoteproxyspec)_ | | | | +| `status` _[api.v1alpha1.MCPRemoteProxyStatus](#apiv1alpha1mcpremoteproxystatus)_ | | | | + +#### api.v1alpha1.MCPRemoteProxyList + +MCPRemoteProxyList contains a list of MCPRemoteProxy + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `MCPRemoteProxyList` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `items` _[api.v1alpha1.MCPRemoteProxy](#apiv1alpha1mcpremoteproxy) array_ | | | | + +#### api.v1alpha1.MCPRemoteProxyPhase + +_Underlying type:_ _string_ + +MCPRemoteProxyPhase is a label for the condition of a MCPRemoteProxy at the current time + +_Validation:_ + +- Enum: [Pending Ready Failed Terminating] + +_Appears in:_ + +- [api.v1alpha1.MCPRemoteProxyStatus](#apiv1alpha1mcpremoteproxystatus) + +| Field | Description | +| ------------- | --------------------------------------------------------------------------------------- | +| `Pending` | MCPRemoteProxyPhasePending means the proxy is being created
| +| `Ready` | MCPRemoteProxyPhaseReady means the proxy is ready and operational
| +| `Failed` | MCPRemoteProxyPhaseFailed means the proxy failed to start or encountered an error
| +| `Terminating` | MCPRemoteProxyPhaseTerminating means the proxy is being deleted
| + +#### api.v1alpha1.MCPRemoteProxySpec + +MCPRemoteProxySpec defines the desired state of MCPRemoteProxy + +_Appears in:_ + +- [api.v1alpha1.MCPRemoteProxy](#apiv1alpha1mcpremoteproxy) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | ------------------------------------------------- | +| `remoteURL` _string_ | RemoteURL is the URL of the remote MCP server to proxy | | Pattern: `^https?://`
Required: \{\}
| +| `port` _integer_ | Port is the port to expose the MCP proxy on
Deprecated: Use ProxyPort instead | 8080 | Maximum: 65535
Minimum: 1
| +| `proxyPort` _integer_ | ProxyPort is the port to expose the MCP proxy on | 8080 | Maximum: 65535
Minimum: 1
| +| `transport` _string_ | Transport is the transport method for the remote proxy (sse or streamable-http) | streamable-http | Enum: [sse streamable-http]
| +| `oidcConfig` _[api.v1alpha1.OIDCConfigRef](#apiv1alpha1oidcconfigref)_ | OIDCConfig defines OIDC authentication configuration for the proxy
This validates incoming tokens from clients. Required for proxy mode. | | Required: \{\}
| +| `externalAuthConfigRef` _[api.v1alpha1.ExternalAuthConfigRef](#apiv1alpha1externalauthconfigref)_ | ExternalAuthConfigRef references a MCPExternalAuthConfig resource for token exchange.
When specified, the proxy will exchange validated incoming tokens for remote service tokens.
The referenced MCPExternalAuthConfig must exist in the same namespace as this MCPRemoteProxy. | | Optional: \{\}
| +| `headerForward` _[api.v1alpha1.HeaderForwardConfig](#apiv1alpha1headerforwardconfig)_ | HeaderForward configures headers to inject into requests to the remote MCP server.
Use this to add custom headers like X-Tenant-ID or correlation IDs. | | Optional: \{\}
| +| `authzConfig` _[api.v1alpha1.AuthzConfigRef](#apiv1alpha1authzconfigref)_ | AuthzConfig defines authorization policy configuration for the proxy | | Optional: \{\}
| +| `audit` _[api.v1alpha1.AuditConfig](#apiv1alpha1auditconfig)_ | Audit defines audit logging configuration for the proxy | | Optional: \{\}
| +| `toolConfigRef` _[api.v1alpha1.ToolConfigRef](#apiv1alpha1toolconfigref)_ | ToolConfigRef references a MCPToolConfig resource for tool filtering and renaming.
The referenced MCPToolConfig must exist in the same namespace as this MCPRemoteProxy.
Cross-namespace references are not supported for security and isolation reasons.
If specified, this allows filtering and overriding tools from the remote MCP server. | | Optional: \{\}
| +| `telemetry` _[api.v1alpha1.TelemetryConfig](#apiv1alpha1telemetryconfig)_ | Telemetry defines observability configuration for the proxy | | Optional: \{\}
| +| `resources` _[api.v1alpha1.ResourceRequirements](#apiv1alpha1resourcerequirements)_ | Resources defines the resource requirements for the proxy container | | Optional: \{\}
| +| `serviceAccount` _string_ | ServiceAccount is the name of an already existing service account to use by the proxy.
If not specified, a ServiceAccount will be created automatically and used by the proxy. | | Optional: \{\}
| +| `trustProxyHeaders` _boolean_ | TrustProxyHeaders indicates whether to trust X-Forwarded-\* headers from reverse proxies
When enabled, the proxy will use X-Forwarded-Proto, X-Forwarded-Host, X-Forwarded-Port,
and X-Forwarded-Prefix headers to construct endpoint URLs | false | Optional: \{\}
| +| `endpointPrefix` _string_ | EndpointPrefix is the path prefix to prepend to SSE endpoint URLs.
This is used to handle path-based ingress routing scenarios where the ingress
strips a path prefix before forwarding to the backend. | | Optional: \{\}
| +| `resourceOverrides` _[api.v1alpha1.ResourceOverrides](#apiv1alpha1resourceoverrides)_ | ResourceOverrides allows overriding annotations and labels for resources created by the operator | | Optional: \{\}
| +| `groupRef` _string_ | GroupRef is the name of the MCPGroup this proxy belongs to
Must reference an existing MCPGroup in the same namespace | | Optional: \{\}
| +| `sessionAffinity` _string_ | SessionAffinity controls whether the Service routes repeated client connections to the same pod.
MCP protocols (SSE, streamable-http) are stateful, so ClientIP is the default.
Set to "None" for stateless servers or when using an external load balancer with its own affinity. | ClientIP | Enum: [ClientIP None]
Optional: \{\}
| + +#### api.v1alpha1.MCPRemoteProxyStatus + +MCPRemoteProxyStatus defines the observed state of MCPRemoteProxy + +_Appears in:_ + +- [api.v1alpha1.MCPRemoteProxy](#apiv1alpha1mcpremoteproxy) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------- | ------- | -------------------------------------------------------------------- | +| `phase` _[api.v1alpha1.MCPRemoteProxyPhase](#apiv1alpha1mcpremoteproxyphase)_ | Phase is the current phase of the MCPRemoteProxy | | Enum: [Pending Ready Failed Terminating]
Optional: \{\}
| +| `url` _string_ | URL is the internal cluster URL where the proxy can be accessed | | Optional: \{\}
| +| `externalURL` _string_ | ExternalURL is the external URL where the proxy can be accessed (if exposed externally) | | Optional: \{\}
| +| `observedGeneration` _integer_ | ObservedGeneration reflects the generation of the most recently observed MCPRemoteProxy | | Optional: \{\}
| +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#condition-v1-meta) array_ | Conditions represent the latest available observations of the MCPRemoteProxy's state | | Optional: \{\}
| +| `toolConfigHash` _string_ | ToolConfigHash stores the hash of the referenced ToolConfig for change detection | | Optional: \{\}
| +| `externalAuthConfigHash` _string_ | ExternalAuthConfigHash is the hash of the referenced MCPExternalAuthConfig spec | | Optional: \{\}
| +| `message` _string_ | Message provides additional information about the current phase | | Optional: \{\}
| + +#### api.v1alpha1.MCPServer + +MCPServer is the Schema for the mcpservers API + +_Appears in:_ + +- [api.v1alpha1.MCPServerList](#apiv1alpha1mcpserverlist) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `MCPServer` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `spec` _[api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec)_ | | | | +| `status` _[api.v1alpha1.MCPServerStatus](#apiv1alpha1mcpserverstatus)_ | | | | + +#### api.v1alpha1.MCPServerList + +MCPServerList contains a list of MCPServer + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `MCPServerList` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `items` _[api.v1alpha1.MCPServer](#apiv1alpha1mcpserver) array_ | | | | + +#### api.v1alpha1.MCPServerPhase + +_Underlying type:_ _string_ + +MCPServerPhase is the phase of the MCPServer + +_Validation:_ + +- Enum: [Pending Running Failed Terminating Stopped] + +_Appears in:_ + +- [api.v1alpha1.MCPServerStatus](#apiv1alpha1mcpserverstatus) + +| Field | Description | +| ------------- | -------------------------------------------------------------------- | +| `Pending` | MCPServerPhasePending means the MCPServer is being created
| +| `Running` | MCPServerPhaseRunning means the MCPServer is running
| +| `Failed` | MCPServerPhaseFailed means the MCPServer failed to start
| +| `Terminating` | MCPServerPhaseTerminating means the MCPServer is being deleted
| +| `Stopped` | MCPServerPhaseStopped means the MCPServer is scaled to zero
| + +#### api.v1alpha1.MCPServerSpec + +MCPServerSpec defines the desired state of MCPServer + +_Appears in:_ + +- [api.v1alpha1.MCPServer](#apiv1alpha1mcpserver) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------- | ----------------------------------------------------------- | +| `image` _string_ | Image is the container image for the MCP server | | Required: \{\}
| +| `transport` _string_ | Transport is the transport method for the MCP server (stdio, streamable-http or sse) | stdio | Enum: [stdio streamable-http sse]
| +| `proxyMode` _string_ | ProxyMode is the proxy mode for stdio transport (sse or streamable-http)
This setting is ONLY applicable when Transport is "stdio".
For direct transports (sse, streamable-http), this field is ignored.
The default value is applied by Kubernetes but will be ignored for non-stdio transports. | streamable-http | Enum: [sse streamable-http]
Optional: \{\}
| +| `port` _integer_ | Port is the port to expose the MCP server on
Deprecated: Use ProxyPort instead | 8080 | Maximum: 65535
Minimum: 1
| +| `targetPort` _integer_ | TargetPort is the port that MCP server listens to
Deprecated: Use McpPort instead | | Maximum: 65535
Minimum: 1
Optional: \{\}
| +| `proxyPort` _integer_ | ProxyPort is the port to expose the proxy runner on | 8080 | Maximum: 65535
Minimum: 1
| +| `mcpPort` _integer_ | McpPort is the port that MCP server listens to | | Maximum: 65535
Minimum: 1
Optional: \{\}
| +| `args` _string array_ | Args are additional arguments to pass to the MCP server | | Optional: \{\}
| +| `env` _[api.v1alpha1.EnvVar](#apiv1alpha1envvar) array_ | Env are environment variables to set in the MCP server container | | Optional: \{\}
| +| `volumes` _[api.v1alpha1.Volume](#apiv1alpha1volume) array_ | Volumes are volumes to mount in the MCP server container | | Optional: \{\}
| +| `resources` _[api.v1alpha1.ResourceRequirements](#apiv1alpha1resourcerequirements)_ | Resources defines the resource requirements for the MCP server container | | Optional: \{\}
| +| `secrets` _[api.v1alpha1.SecretRef](#apiv1alpha1secretref) array_ | Secrets are references to secrets to mount in the MCP server container | | Optional: \{\}
| +| `serviceAccount` _string_ | ServiceAccount is the name of an already existing service account to use by the MCP server.
If not specified, a ServiceAccount will be created automatically and used by the MCP server. | | Optional: \{\}
| +| `permissionProfile` _[api.v1alpha1.PermissionProfileRef](#apiv1alpha1permissionprofileref)_ | PermissionProfile defines the permission profile to use | | Optional: \{\}
| +| `podTemplateSpec` _[RawExtension](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#rawextension-runtime-pkg)_ | PodTemplateSpec defines the pod template to use for the MCP server
This allows for customizing the pod configuration beyond what is provided by the other fields.
Note that to modify the specific container the MCP server runs in, you must specify
the `mcp` container name in the PodTemplateSpec.
This field accepts a PodTemplateSpec object as JSON/YAML. | | Type: object
Optional: \{\}
| +| `resourceOverrides` _[api.v1alpha1.ResourceOverrides](#apiv1alpha1resourceoverrides)_ | ResourceOverrides allows overriding annotations and labels for resources created by the operator | | Optional: \{\}
| +| `oidcConfig` _[api.v1alpha1.OIDCConfigRef](#apiv1alpha1oidcconfigref)_ | OIDCConfig defines OIDC authentication configuration for the MCP server | | Optional: \{\}
| +| `authzConfig` _[api.v1alpha1.AuthzConfigRef](#apiv1alpha1authzconfigref)_ | AuthzConfig defines authorization policy configuration for the MCP server | | Optional: \{\}
| +| `audit` _[api.v1alpha1.AuditConfig](#apiv1alpha1auditconfig)_ | Audit defines audit logging configuration for the MCP server | | Optional: \{\}
| +| `tools` _string array_ | ToolsFilter is the filter on tools applied to the MCP server
Deprecated: Use ToolConfigRef instead | | Optional: \{\}
| +| `toolConfigRef` _[api.v1alpha1.ToolConfigRef](#apiv1alpha1toolconfigref)_ | ToolConfigRef references a MCPToolConfig resource for tool filtering and renaming.
The referenced MCPToolConfig must exist in the same namespace as this MCPServer.
Cross-namespace references are not supported for security and isolation reasons.
If specified, this takes precedence over the inline ToolsFilter field. | | Optional: \{\}
| +| `externalAuthConfigRef` _[api.v1alpha1.ExternalAuthConfigRef](#apiv1alpha1externalauthconfigref)_ | ExternalAuthConfigRef references a MCPExternalAuthConfig resource for external authentication.
The referenced MCPExternalAuthConfig must exist in the same namespace as this MCPServer. | | Optional: \{\}
| +| `telemetry` _[api.v1alpha1.TelemetryConfig](#apiv1alpha1telemetryconfig)_ | Telemetry defines observability configuration for the MCP server | | Optional: \{\}
| +| `trustProxyHeaders` _boolean_ | TrustProxyHeaders indicates whether to trust X-Forwarded-\* headers from reverse proxies
When enabled, the proxy will use X-Forwarded-Proto, X-Forwarded-Host, X-Forwarded-Port,
and X-Forwarded-Prefix headers to construct endpoint URLs | false | Optional: \{\}
| +| `endpointPrefix` _string_ | EndpointPrefix is the path prefix to prepend to SSE endpoint URLs.
This is used to handle path-based ingress routing scenarios where the ingress
strips a path prefix before forwarding to the backend. | | Optional: \{\}
| +| `groupRef` _string_ | GroupRef is the name of the MCPGroup this server belongs to
Must reference an existing MCPGroup in the same namespace | | Optional: \{\}
| +| `sessionAffinity` _string_ | SessionAffinity controls whether the Service routes repeated client connections to the same pod.
MCP protocols (SSE, streamable-http) are stateful, so ClientIP is the default.
Set to "None" for stateless servers or when using an external load balancer with its own affinity. | ClientIP | Enum: [ClientIP None]
Optional: \{\}
| +| `replicas` _integer_ | Replicas is the desired number of proxy runner (thv run) pod replicas.
MCPServer creates two separate Deployments: one for the proxy runner and one
for the MCP server backend. This field controls the proxy runner Deployment.
When nil, the operator does not set Deployment.Spec.Replicas, leaving replica
management to an HPA or other external controller. | | Minimum: 0
Optional: \{\}
| +| `backendReplicas` _integer_ | BackendReplicas is the desired number of MCP server backend pod replicas.
This controls the backend Deployment (the MCP server container itself),
independent of the proxy runner controlled by Replicas.
When nil, the operator does not set Deployment.Spec.Replicas, leaving replica
management to an HPA or other external controller. | | Minimum: 0
Optional: \{\}
| +| `sessionStorage` _[api.v1alpha1.SessionStorageConfig](#apiv1alpha1sessionstorageconfig)_ | SessionStorage configures session storage for stateful horizontal scaling.
When nil, no session storage is configured. | | Optional: \{\}
| + +#### api.v1alpha1.MCPServerStatus + +MCPServerStatus defines the observed state of MCPServer + +_Appears in:_ + +- [api.v1alpha1.MCPServer](#apiv1alpha1mcpserver) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------ | +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#condition-v1-meta) array_ | Conditions represent the latest available observations of the MCPServer's state | | Optional: \{\}
| +| `toolConfigHash` _string_ | ToolConfigHash stores the hash of the referenced ToolConfig for change detection | | Optional: \{\}
| +| `externalAuthConfigHash` _string_ | ExternalAuthConfigHash is the hash of the referenced MCPExternalAuthConfig spec | | Optional: \{\}
| +| `url` _string_ | URL is the URL where the MCP server can be accessed | | Optional: \{\}
| +| `phase` _[api.v1alpha1.MCPServerPhase](#apiv1alpha1mcpserverphase)_ | Phase is the current phase of the MCPServer | | Enum: [Pending Running Failed Terminating Stopped]
Optional: \{\}
| +| `message` _string_ | Message provides additional information about the current phase | | Optional: \{\}
| +| `readyReplicas` _integer_ | ReadyReplicas is the number of ready proxy replicas | | Optional: \{\}
| + +#### api.v1alpha1.MCPToolConfig + +MCPToolConfig is the Schema for the mcptoolconfigs API. +MCPToolConfig resources are namespace-scoped and can only be referenced by +MCPServer resources within the same namespace. Cross-namespace references +are not supported for security and isolation reasons. + +_Appears in:_ + +- [api.v1alpha1.MCPToolConfigList](#apiv1alpha1mcptoolconfiglist) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `MCPToolConfig` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `spec` _[api.v1alpha1.MCPToolConfigSpec](#apiv1alpha1mcptoolconfigspec)_ | | | | +| `status` _[api.v1alpha1.MCPToolConfigStatus](#apiv1alpha1mcptoolconfigstatus)_ | | | | + +#### api.v1alpha1.MCPToolConfigList + +MCPToolConfigList contains a list of MCPToolConfig + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `MCPToolConfigList` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `items` _[api.v1alpha1.MCPToolConfig](#apiv1alpha1mcptoolconfig) array_ | | | | + +#### api.v1alpha1.MCPToolConfigSpec + +MCPToolConfigSpec defines the desired state of MCPToolConfig. +MCPToolConfig resources are namespace-scoped and can only be referenced by +MCPServer resources in the same namespace. + +_Appears in:_ + +- [api.v1alpha1.MCPToolConfig](#apiv1alpha1mcptoolconfig) + +| Field | Description | Default | Validation | +| ---------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `toolsFilter` _string array_ | ToolsFilter is a list of tool names to filter (allow list).
Only tools in this list will be exposed by the MCP server.
If empty, all tools are exposed. | | Optional: \{\}
| +| `toolsOverride` _object (keys:string, values:[api.v1alpha1.ToolOverride](#apiv1alpha1tooloverride))_ | ToolsOverride is a map from actual tool names to their overridden configuration.
This allows renaming tools and/or changing their descriptions. | | Optional: \{\}
| + +#### api.v1alpha1.MCPToolConfigStatus + +MCPToolConfigStatus defines the observed state of MCPToolConfig + +_Appears in:_ + +- [api.v1alpha1.MCPToolConfig](#apiv1alpha1mcptoolconfig) + +| Field | Description | Default | Validation | +| ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `observedGeneration` _integer_ | ObservedGeneration is the most recent generation observed for this MCPToolConfig.
It corresponds to the MCPToolConfig's generation, which is updated on mutation by the API Server. | | Optional: \{\}
| +| `configHash` _string_ | ConfigHash is a hash of the current configuration for change detection | | Optional: \{\}
| +| `referencingServers` _string array_ | ReferencingServers is a list of MCPServer resources that reference this MCPToolConfig
This helps track which servers need to be reconciled when this config changes | | Optional: \{\}
| + +#### api.v1alpha1.ModelCacheConfig + +ModelCacheConfig configures persistent storage for model caching + +_Appears in:_ + +- [api.v1alpha1.EmbeddingServerSpec](#apiv1alpha1embeddingserverspec) + +| Field | Description | Default | Validation | +| --------------------------- | ------------------------------------------------------------------------------------------------------------------------ | ------------- | ---------------------------------------------------------------------------- | +| `enabled` _boolean_ | Enabled controls whether model caching is enabled | true | Optional: \{\}
| +| `storageClassName` _string_ | StorageClassName is the storage class to use for the PVC
If not specified, uses the cluster's default storage class | | Optional: \{\}
| +| `size` _string_ | Size is the size of the PVC for model caching (e.g., "10Gi") | 10Gi | Optional: \{\}
| +| `accessMode` _string_ | AccessMode is the access mode for the PVC | ReadWriteOnce | Enum: [ReadWriteOnce ReadWriteMany ReadOnlyMany]
Optional: \{\}
| + +#### api.v1alpha1.NameFilter + +NameFilter defines name-based filtering + +_Appears in:_ + +- [api.v1alpha1.RegistryFilter](#apiv1alpha1registryfilter) + +| Field | Description | Default | Validation | +| ------------------------ | --------------------------------------------- | ------- | --------------------- | +| `include` _string array_ | Include is a list of glob patterns to include | | Optional: \{\}
| +| `exclude` _string array_ | Exclude is a list of glob patterns to exclude | | Optional: \{\}
| + +#### api.v1alpha1.NetworkPermissions + +NetworkPermissions defines the network permissions for an MCP server + +_Appears in:_ + +- [api.v1alpha1.PermissionProfileSpec](#apiv1alpha1permissionprofilespec) + +| Field | Description | Default | Validation | +| ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ | ------- | --------------------- | +| `mode` _string_ | Mode specifies the network mode for the container (e.g., "host", "bridge", "none")
When empty, the default container runtime network mode is used | | Optional: \{\}
| +| `outbound` _[api.v1alpha1.OutboundNetworkPermissions](#apiv1alpha1outboundnetworkpermissions)_ | Outbound defines the outbound network permissions | | Optional: \{\}
| + +#### api.v1alpha1.OAuth2UpstreamConfig + +OAuth2UpstreamConfig contains configuration for pure OAuth 2.0 providers. +OAuth 2.0 providers require explicit endpoint configuration. + +_Appears in:_ + +- [api.v1alpha1.UpstreamProviderConfig](#apiv1alpha1upstreamproviderconfig) + +| Field | Description | Default | Validation | +| ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------------------- | +| `authorizationEndpoint` _string_ | AuthorizationEndpoint is the URL for the OAuth authorization endpoint. | | Pattern: `^https?://.*$`
Required: \{\}
| +| `tokenEndpoint` _string_ | TokenEndpoint is the URL for the OAuth token endpoint. | | Pattern: `^https?://.*$`
Required: \{\}
| +| `userInfo` _[api.v1alpha1.UserInfoConfig](#apiv1alpha1userinfoconfig)_ | UserInfo contains configuration for fetching user information from the upstream provider.
Required for OAuth2 providers to resolve user identity. | | Required: \{\}
| +| `clientId` _string_ | ClientID is the OAuth 2.0 client identifier registered with the upstream IDP. | | Required: \{\}
| +| `clientSecretRef` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref)_ | ClientSecretRef references a Kubernetes Secret containing the OAuth 2.0 client secret.
Optional for public clients using PKCE instead of client secret. | | Optional: \{\}
| +| `redirectUri` _string_ | RedirectURI is the callback URL where the upstream IDP will redirect after authentication.
When not specified, defaults to `\{resourceUrl\}/oauth/callback` where `resourceUrl` is the
URL associated with the resource (e.g., MCPServer or vMCP) using this config. | | Optional: \{\}
| +| `scopes` _string array_ | Scopes are the OAuth scopes to request from the upstream IDP. | | Optional: \{\}
| +| `tokenResponseMapping` _[api.v1alpha1.TokenResponseMapping](#apiv1alpha1tokenresponsemapping)_ | TokenResponseMapping configures custom field extraction from non-standard token responses.
Some OAuth providers (e.g., GovSlack) nest token fields under non-standard paths
instead of returning them at the top level. When set, ToolHive performs the token
exchange HTTP call directly and extracts fields using the configured dot-notation paths.
If nil, standard OAuth 2.0 token response parsing is used. | | Optional: \{\}
| + +#### api.v1alpha1.OIDCConfigRef + +OIDCConfigRef defines a reference to OIDC configuration + +_Appears in:_ + +- [api.v1alpha1.IncomingAuthConfig](#apiv1alpha1incomingauthconfig) +- [api.v1alpha1.MCPRemoteProxySpec](#apiv1alpha1mcpremoteproxyspec) +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ------------------------------------------ | +| `type` _string_ | Type is the type of OIDC configuration | kubernetes | Enum: [kubernetes configMap inline]
| +| `resourceUrl` _string_ | ResourceURL is the explicit resource URL for OAuth discovery endpoint (RFC 9728)
If not specified, defaults to the in-cluster Kubernetes service URL | | Optional: \{\}
| +| `kubernetes` _[api.v1alpha1.KubernetesOIDCConfig](#apiv1alpha1kubernetesoidcconfig)_ | Kubernetes configures OIDC for Kubernetes service account token validation
Only used when Type is "kubernetes" | | Optional: \{\}
| +| `configMap` _[api.v1alpha1.ConfigMapOIDCRef](#apiv1alpha1configmapoidcref)_ | ConfigMap references a ConfigMap containing OIDC configuration
Only used when Type is "configmap" | | Optional: \{\}
| +| `inline` _[api.v1alpha1.InlineOIDCConfig](#apiv1alpha1inlineoidcconfig)_ | Inline contains direct OIDC configuration
Only used when Type is "inline" | | Optional: \{\}
| + +#### api.v1alpha1.OIDCUpstreamConfig + +OIDCUpstreamConfig contains configuration for OIDC providers. +OIDC providers support automatic endpoint discovery via the issuer URL. + +_Appears in:_ + +- [api.v1alpha1.UpstreamProviderConfig](#apiv1alpha1upstreamproviderconfig) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------------------------------------- | +| `issuerUrl` _string_ | IssuerURL is the OIDC issuer URL for automatic endpoint discovery.
Must be a valid HTTPS URL. | | Pattern: `^https://.*$`
Required: \{\}
| +| `clientId` _string_ | ClientID is the OAuth 2.0 client identifier registered with the upstream IDP. | | Required: \{\}
| +| `clientSecretRef` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref)_ | ClientSecretRef references a Kubernetes Secret containing the OAuth 2.0 client secret.
Optional for public clients using PKCE instead of client secret. | | Optional: \{\}
| +| `redirectUri` _string_ | RedirectURI is the callback URL where the upstream IDP will redirect after authentication.
When not specified, defaults to `\{resourceUrl\}/oauth/callback` where `resourceUrl` is the
URL associated with the resource (e.g., MCPServer or vMCP) using this config. | | Optional: \{\}
| +| `scopes` _string array_ | Scopes are the OAuth scopes to request from the upstream IDP.
If not specified, defaults to ["openid", "offline_access"]. | | Optional: \{\}
| +| `userInfoOverride` _[api.v1alpha1.UserInfoConfig](#apiv1alpha1userinfoconfig)_ | UserInfoOverride allows customizing UserInfo fetching behavior for OIDC providers.
By default, the UserInfo endpoint is discovered automatically via OIDC discovery.
Use this to override the endpoint URL, HTTP method, or field mappings for providers
that return non-standard claim names in their UserInfo response. | | Optional: \{\}
| + +#### api.v1alpha1.OpenTelemetryConfig + +OpenTelemetryConfig defines pure OpenTelemetry configuration + +_Appears in:_ + +- [api.v1alpha1.TelemetryConfig](#apiv1alpha1telemetryconfig) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `enabled` _boolean_ | Enabled controls whether OpenTelemetry is enabled | false | Optional: \{\}
| +| `endpoint` _string_ | Endpoint is the OTLP endpoint URL for tracing and metrics | | Optional: \{\}
| +| `serviceName` _string_ | ServiceName is the service name for telemetry
If not specified, defaults to the MCPServer name | | Optional: \{\}
| +| `headers` _string array_ | Headers contains authentication headers for the OTLP endpoint
Specified as key=value pairs | | Optional: \{\}
| +| `insecure` _boolean_ | Insecure indicates whether to use HTTP instead of HTTPS for the OTLP endpoint | false | Optional: \{\}
| +| `metrics` _[api.v1alpha1.OpenTelemetryMetricsConfig](#apiv1alpha1opentelemetrymetricsconfig)_ | Metrics defines OpenTelemetry metrics-specific configuration | | Optional: \{\}
| +| `tracing` _[api.v1alpha1.OpenTelemetryTracingConfig](#apiv1alpha1opentelemetrytracingconfig)_ | Tracing defines OpenTelemetry tracing configuration | | Optional: \{\}
| +| `useLegacyAttributes` _boolean_ | UseLegacyAttributes controls whether legacy attribute names are emitted alongside
the new MCP OTEL semantic convention names. Defaults to true for backward compatibility.
This will change to false in a future release and eventually be removed. | true | Optional: \{\}
| + +#### api.v1alpha1.OpenTelemetryMetricsConfig + +OpenTelemetryMetricsConfig defines OpenTelemetry metrics configuration + +_Appears in:_ + +- [api.v1alpha1.OpenTelemetryConfig](#apiv1alpha1opentelemetryconfig) + +| Field | Description | Default | Validation | +| ------------------- | ---------------------------------------------- | ------- | --------------------- | +| `enabled` _boolean_ | Enabled controls whether OTLP metrics are sent | false | Optional: \{\}
| + +#### api.v1alpha1.OpenTelemetryTracingConfig + +OpenTelemetryTracingConfig defines OpenTelemetry tracing configuration + +_Appears in:_ + +- [api.v1alpha1.OpenTelemetryConfig](#apiv1alpha1opentelemetryconfig) + +| Field | Description | Default | Validation | +| ----------------------- | ------------------------------------------------- | ------- | --------------------- | +| `enabled` _boolean_ | Enabled controls whether OTLP tracing is sent | false | Optional: \{\}
| +| `samplingRate` _string_ | SamplingRate is the trace sampling rate (0.0-1.0) | 0.05 | Optional: \{\}
| + +#### api.v1alpha1.OutboundNetworkPermissions + +OutboundNetworkPermissions defines the outbound network permissions + +_Appears in:_ + +- [api.v1alpha1.NetworkPermissions](#apiv1alpha1networkpermissions) + +| Field | Description | Default | Validation | +| ---------------------------- | -------------------------------------------------------------------------- | ------- | --------------------- | +| `insecureAllowAll` _boolean_ | InsecureAllowAll allows all outbound network connections (not recommended) | false | Optional: \{\}
| +| `allowHost` _string array_ | AllowHost is a list of hosts to allow connections to | | Optional: \{\}
| +| `allowPort` _integer array_ | AllowPort is a list of ports to allow connections to | | Optional: \{\}
| + +#### api.v1alpha1.OutgoingAuthConfig + +OutgoingAuthConfig configures authentication from Virtual MCP to backend MCPServers + +_Appears in:_ + +- [api.v1alpha1.VirtualMCPServerSpec](#apiv1alpha1virtualmcpserverspec) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------- | ----------------------------------------------------- | +| `source` _string_ | Source defines how backend authentication configurations are determined
- discovered: Automatically discover from backend's MCPServer.spec.externalAuthConfigRef
- inline: Explicit per-backend configuration in VirtualMCPServer | discovered | Enum: [discovered inline]
Optional: \{\}
| +| `default` _[api.v1alpha1.BackendAuthConfig](#apiv1alpha1backendauthconfig)_ | Default defines default behavior for backends without explicit auth config | | Optional: \{\}
| +| `backends` _object (keys:string, values:[api.v1alpha1.BackendAuthConfig](#apiv1alpha1backendauthconfig))_ | Backends defines per-backend authentication overrides
Works in all modes (discovered, inline) | | Optional: \{\}
| + +#### api.v1alpha1.PVCSource + +PVCSource defines PersistentVolumeClaim source configuration + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryConfig](#apiv1alpha1mcpregistryconfig) + +| Field | Description | Default | Validation | +| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------- | ------------------------------------------------- | +| `claimName` _string_ | ClaimName is the name of the PersistentVolumeClaim | | MinLength: 1
Required: \{\}
| +| `path` _string_ | Path is the relative path to the registry file within the PVC.
The PVC is mounted at /config/registry/\{registryName\}/.
The full file path becomes: /config/registry/\{registryName\}/\{path\}
This design:
- Each registry gets its own mount point (consistent with ConfigMap sources)
- Multiple registries can share the same PVC by mounting it at different paths
- Users control PVC organization freely via the path field
Examples:
Registry "production" using PVC "shared-data" with path "prod/registry.json":
PVC contains /prod/registry.json → accessed at /config/registry/production/prod/registry.json
Registry "development" using SAME PVC "shared-data" with path "dev/registry.json":
PVC contains /dev/registry.json → accessed at /config/registry/development/dev/registry.json
(Same PVC, different mount path)
Registry "staging" using DIFFERENT PVC "other-pvc" with path "registry.json":
PVC contains /registry.json → accessed at /config/registry/staging/registry.json
(Different PVC, independent mount)
Registry "team-a" with path "v1/servers.json":
PVC contains /v1/servers.json → accessed at /config/registry/team-a/v1/servers.json
(Subdirectories allowed in path) | registry.json | Pattern: `^.*\.json$`
Optional: \{\}
| + +#### api.v1alpha1.PermissionProfileRef + +PermissionProfileRef defines a reference to a permission profile + +_Appears in:_ + +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) + +| Field | Description | Default | Validation | +| --------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -------------------------------- | +| `type` _string_ | Type is the type of permission profile reference | builtin | Enum: [builtin configmap]
| +| `name` _string_ | Name is the name of the permission profile
If Type is "builtin", Name must be one of: "none", "network"
If Type is "configmap", Name is the name of the ConfigMap | | Required: \{\}
| +| `key` _string_ | Key is the key in the ConfigMap that contains the permission profile
Only used when Type is "configmap" | | Optional: \{\}
| + +#### api.v1alpha1.PrometheusConfig + +PrometheusConfig defines Prometheus-specific configuration + +_Appears in:_ + +- [api.v1alpha1.TelemetryConfig](#apiv1alpha1telemetryconfig) + +| Field | Description | Default | Validation | +| ------------------- | --------------------------------------------------------------- | ------- | --------------------- | +| `enabled` _boolean_ | Enabled controls whether Prometheus metrics endpoint is exposed | false | Optional: \{\}
| + +#### api.v1alpha1.ProxyDeploymentOverrides + +ProxyDeploymentOverrides defines overrides specific to the proxy deployment + +_Appears in:_ + +- [api.v1alpha1.ResourceOverrides](#apiv1alpha1resourceoverrides) + +| Field | Description | Default | Validation | +| ---------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `annotations` _object (keys:string, values:string)_ | Annotations to add or override on the resource | | Optional: \{\}
| +| `labels` _object (keys:string, values:string)_ | Labels to add or override on the resource | | Optional: \{\}
| +| `podTemplateMetadataOverrides` _[api.v1alpha1.ResourceMetadataOverrides](#apiv1alpha1resourcemetadataoverrides)_ | | | | +| `env` _[api.v1alpha1.EnvVar](#apiv1alpha1envvar) array_ | Env are environment variables to set in the proxy container (thv run process)
These affect the toolhive proxy itself, not the MCP server it manages
Use TOOLHIVE_DEBUG=true to enable debug logging in the proxy | | Optional: \{\}
| +| `imagePullSecrets` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#localobjectreference-v1-core) array_ | ImagePullSecrets allows specifying image pull secrets for the proxy runner
These are applied to both the Deployment and the ServiceAccount | | Optional: \{\}
| + +#### api.v1alpha1.RedisACLUserConfig + +RedisACLUserConfig configures Redis ACL user authentication. + +_Appears in:_ + +- [api.v1alpha1.RedisStorageConfig](#apiv1alpha1redisstorageconfig) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------- | ------------------------------------------------------------------------ | ------- | --------------------- | +| `usernameSecretRef` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref)_ | UsernameSecretRef references a Secret containing the Redis ACL username. | | Required: \{\}
| +| `passwordSecretRef` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref)_ | PasswordSecretRef references a Secret containing the Redis ACL password. | | Required: \{\}
| + +#### api.v1alpha1.RedisSentinelConfig + +RedisSentinelConfig configures Redis Sentinel connection. + +_Appears in:_ + +- [api.v1alpha1.RedisStorageConfig](#apiv1alpha1redisstorageconfig) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ | ------- | --------------------- | +| `masterName` _string_ | MasterName is the name of the Redis master monitored by Sentinel. | | Required: \{\}
| +| `sentinelAddrs` _string array_ | SentinelAddrs is a list of Sentinel host:port addresses.
Mutually exclusive with SentinelService. | | Optional: \{\}
| +| `sentinelService` _[api.v1alpha1.SentinelServiceRef](#apiv1alpha1sentinelserviceref)_ | SentinelService enables automatic discovery from a Kubernetes Service.
Mutually exclusive with SentinelAddrs. | | Optional: \{\}
| +| `db` _integer_ | DB is the Redis database number. | 0 | Optional: \{\}
| + +#### api.v1alpha1.RedisStorageConfig + +RedisStorageConfig configures Redis connection for auth server storage. +Redis is deployed in Sentinel mode with ACL user authentication (the only supported configuration). + +_Appears in:_ + +- [api.v1alpha1.AuthServerStorageConfig](#apiv1alpha1authserverstorageconfig) + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------- | +| `sentinelConfig` _[api.v1alpha1.RedisSentinelConfig](#apiv1alpha1redissentinelconfig)_ | SentinelConfig holds Redis Sentinel configuration. | | Required: \{\}
| +| `aclUserConfig` _[api.v1alpha1.RedisACLUserConfig](#apiv1alpha1redisacluserconfig)_ | ACLUserConfig configures Redis ACL user authentication. | | Required: \{\}
| +| `dialTimeout` _string_ | DialTimeout is the timeout for establishing connections.
Format: Go duration string (e.g., "5s", "1m"). | 5s | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Optional: \{\}
| +| `readTimeout` _string_ | ReadTimeout is the timeout for socket reads.
Format: Go duration string (e.g., "3s", "1m"). | 3s | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Optional: \{\}
| +| `writeTimeout` _string_ | WriteTimeout is the timeout for socket writes.
Format: Go duration string (e.g., "3s", "1m"). | 3s | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Optional: \{\}
| +| `tls` _[api.v1alpha1.RedisTLSConfig](#apiv1alpha1redistlsconfig)_ | TLS configures TLS for connections to the Redis/Valkey master.
Presence of this field enables TLS. Omit to use plaintext. | | Optional: \{\}
| +| `sentinelTls` _[api.v1alpha1.RedisTLSConfig](#apiv1alpha1redistlsconfig)_ | SentinelTLS configures TLS for connections to Sentinel instances.
Presence of this field enables TLS. Omit to use plaintext.
When omitted, sentinel connections use plaintext (no fallback to TLS config). | | Optional: \{\}
| + +#### api.v1alpha1.RedisTLSConfig + +RedisTLSConfig configures TLS for Redis connections. +Presence of this struct on a connection type enables TLS for that connection. + +_Appears in:_ + +- [api.v1alpha1.RedisStorageConfig](#apiv1alpha1redisstorageconfig) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `insecureSkipVerify` _boolean_ | InsecureSkipVerify skips TLS certificate verification.
Use when connecting to services with self-signed certificates. | | Optional: \{\}
| +| `caCertSecretRef` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref)_ | CACertSecretRef references a Secret containing a PEM-encoded CA certificate
for verifying the server. When not specified, system root CAs are used. | | Optional: \{\}
| + +#### api.v1alpha1.RegistryFilter + +RegistryFilter defines include/exclude patterns for registry content + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryConfig](#apiv1alpha1mcpregistryconfig) + +| Field | Description | Default | Validation | +| ----------------------------------------------------------- | ---------------------------------------- | ------- | --------------------- | +| `names` _[api.v1alpha1.NameFilter](#apiv1alpha1namefilter)_ | NameFilters defines name-based filtering | | Optional: \{\}
| +| `tags` _[api.v1alpha1.TagFilter](#apiv1alpha1tagfilter)_ | Tags defines tag-based filtering | | Optional: \{\}
| + +#### api.v1alpha1.ResourceList + +ResourceList is a set of (resource name, quantity) pairs + +_Appears in:_ + +- [api.v1alpha1.ResourceRequirements](#apiv1alpha1resourcerequirements) + +| Field | Description | Default | Validation | +| ----------------- | ------------------------------------------------------------------- | ------- | --------------------- | +| `cpu` _string_ | CPU is the CPU limit in cores (e.g., "500m" for 0.5 cores) | | Optional: \{\}
| +| `memory` _string_ | Memory is the memory limit in bytes (e.g., "64Mi" for 64 megabytes) | | Optional: \{\}
| + +#### api.v1alpha1.ResourceMetadataOverrides + +ResourceMetadataOverrides defines metadata overrides for a resource + +_Appears in:_ + +- [api.v1alpha1.EmbeddingResourceOverrides](#apiv1alpha1embeddingresourceoverrides) +- [api.v1alpha1.EmbeddingStatefulSetOverrides](#apiv1alpha1embeddingstatefulsetoverrides) +- [api.v1alpha1.ProxyDeploymentOverrides](#apiv1alpha1proxydeploymentoverrides) +- [api.v1alpha1.ResourceOverrides](#apiv1alpha1resourceoverrides) + +| Field | Description | Default | Validation | +| --------------------------------------------------- | ---------------------------------------------- | ------- | --------------------- | +| `annotations` _object (keys:string, values:string)_ | Annotations to add or override on the resource | | Optional: \{\}
| +| `labels` _object (keys:string, values:string)_ | Labels to add or override on the resource | | Optional: \{\}
| + +#### api.v1alpha1.ResourceOverrides + +ResourceOverrides defines overrides for annotations and labels on created resources + +_Appears in:_ + +- [api.v1alpha1.MCPRemoteProxySpec](#apiv1alpha1mcpremoteproxyspec) +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `proxyDeployment` _[api.v1alpha1.ProxyDeploymentOverrides](#apiv1alpha1proxydeploymentoverrides)_ | ProxyDeployment defines overrides for the Proxy Deployment resource (toolhive proxy) | | Optional: \{\}
| +| `proxyService` _[api.v1alpha1.ResourceMetadataOverrides](#apiv1alpha1resourcemetadataoverrides)_ | ProxyService defines overrides for the Proxy Service resource (points to the proxy deployment) | | Optional: \{\}
| + +#### api.v1alpha1.ResourceRequirements + +ResourceRequirements describes the compute resource requirements + +_Appears in:_ + +- [api.v1alpha1.EmbeddingServerSpec](#apiv1alpha1embeddingserverspec) +- [api.v1alpha1.MCPRemoteProxySpec](#apiv1alpha1mcpremoteproxyspec) +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------ | ------------------------------------------------------------------- | ------- | --------------------- | +| `limits` _[api.v1alpha1.ResourceList](#apiv1alpha1resourcelist)_ | Limits describes the maximum amount of compute resources allowed | | Optional: \{\}
| +| `requests` _[api.v1alpha1.ResourceList](#apiv1alpha1resourcelist)_ | Requests describes the minimum amount of compute resources required | | Optional: \{\}
| + +#### api.v1alpha1.RoleMapping + +RoleMapping defines a rule for mapping JWT claims to IAM roles. +Mappings are evaluated in priority order (lower number = higher priority), and the first +matching rule determines which IAM role to assume. +Exactly one of Claim or Matcher must be specified. + +_Appears in:_ + +- [api.v1alpha1.AWSStsConfig](#apiv1alpha1awsstsconfig) + +| Field | Description | Default | Validation | +| -------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------------------------- | +| `claim` _string_ | Claim is a simple claim value to match against
The claim type is specified by AWSStsConfig.RoleClaim
For example, if RoleClaim is "groups", this would be a group name
Internally compiled to a CEL expression: "" in claims[""]
Mutually exclusive with Matcher | | MinLength: 1
Optional: \{\}
| +| `matcher` _string_ | Matcher is a CEL expression for complex matching against JWT claims
The expression has access to a "claims" variable containing all JWT claims as map[string]any
Examples:
- "admins" in claims["groups"]
- claims["sub"] == "user123" && !("act" in claims)
Mutually exclusive with Claim | | MinLength: 1
Optional: \{\}
| +| `roleArn` _string_ | RoleArn is the IAM role ARN to assume when this mapping matches | | Pattern: `^arn:(aws\|aws-cn\|aws-us-gov):iam::\d\{12\}:role/[\w+=,.@\-_/]+$`
Required: \{\}
| +| `priority` _integer_ | Priority determines evaluation order (lower values = higher priority)
Allows fine-grained control over role selection precedence
When omitted, this mapping has the lowest possible priority and
configuration order acts as tie-breaker via stable sort | | Minimum: 0
Optional: \{\}
| + +#### api.v1alpha1.SecretKeyRef + +SecretKeyRef is a reference to a key within a Secret + +_Appears in:_ + +- [api.v1alpha1.BearerTokenConfig](#apiv1alpha1bearertokenconfig) +- [api.v1alpha1.EmbeddedAuthServerConfig](#apiv1alpha1embeddedauthserverconfig) +- [api.v1alpha1.EmbeddingServerSpec](#apiv1alpha1embeddingserverspec) +- [api.v1alpha1.HeaderFromSecret](#apiv1alpha1headerfromsecret) +- [api.v1alpha1.HeaderInjectionConfig](#apiv1alpha1headerinjectionconfig) +- [api.v1alpha1.InlineOIDCConfig](#apiv1alpha1inlineoidcconfig) +- [api.v1alpha1.OAuth2UpstreamConfig](#apiv1alpha1oauth2upstreamconfig) +- [api.v1alpha1.OIDCUpstreamConfig](#apiv1alpha1oidcupstreamconfig) +- [api.v1alpha1.RedisACLUserConfig](#apiv1alpha1redisacluserconfig) +- [api.v1alpha1.RedisTLSConfig](#apiv1alpha1redistlsconfig) +- [api.v1alpha1.SessionStorageConfig](#apiv1alpha1sessionstorageconfig) +- [api.v1alpha1.TokenExchangeConfig](#apiv1alpha1tokenexchangeconfig) + +| Field | Description | Default | Validation | +| --------------- | -------------------------------- | ------- | --------------------- | +| `name` _string_ | Name is the name of the secret | | Required: \{\}
| +| `key` _string_ | Key is the key within the secret | | Required: \{\}
| + +#### api.v1alpha1.SecretRef + +SecretRef is a reference to a secret + +_Appears in:_ + +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) + +| Field | Description | Default | Validation | +| ------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `name` _string_ | Name is the name of the secret | | Required: \{\}
| +| `key` _string_ | Key is the key in the secret itself | | Required: \{\}
| +| `targetEnvName` _string_ | TargetEnvName is the environment variable to be used when setting up the secret in the MCP server
If left unspecified, it defaults to the key | | Optional: \{\}
| + +#### api.v1alpha1.SentinelServiceRef + +_Underlying type:_ _[api.v1alpha1.struct{Name string "json:\"name\""; Namespace string "json:\"namespace,omitempty\""; Port int32 "json:\"port,omitempty\""}](#apiv1alpha1struct{name string "json:\"name\""; namespace string "json:\"namespace,omitempty\""; port int32 "json:\"port,omitempty\""})_ + +SentinelServiceRef references a Kubernetes Service for Sentinel discovery. + +_Appears in:_ + +- [api.v1alpha1.RedisSentinelConfig](#apiv1alpha1redissentinelconfig) + +#### api.v1alpha1.SessionStorageConfig + +SessionStorageConfig defines session storage configuration for horizontal scaling. + +This is the CRD/K8s-aware surface: it uses SecretKeyRef for secret resolution. +The reconciler resolves PasswordRef to a plain string and builds a +session.RedisConfig (pkg/transport/session) for the actual storage backend. +The operator also populates pkg/vmcp/config.SessionStorageConfig (without PasswordRef) +into the vMCP ConfigMap so the vMCP process receives connection parameters at startup. + +_Appears in:_ + +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) +- [api.v1alpha1.VirtualMCPServerSpec](#apiv1alpha1virtualmcpserverspec) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------- | ------------------------------------------------------------------------ | ------- | ------------------------------------------------ | +| `provider` _string_ | Provider is the session storage backend type | | Enum: [memory redis]
Required: \{\}
| +| `address` _string_ | Address is the Redis server address (required when provider is redis) | | MinLength: 1
Optional: \{\}
| +| `db` _integer_ | DB is the Redis database number | 0 | Minimum: 0
Optional: \{\}
| +| `keyPrefix` _string_ | KeyPrefix is an optional prefix for all Redis keys used by ToolHive | | Optional: \{\}
| +| `passwordRef` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref)_ | PasswordRef is a reference to a Secret key containing the Redis password | | Optional: \{\}
| + +#### api.v1alpha1.StorageReference + +StorageReference defines a reference to internal storage + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryStatus](#apiv1alpha1mcpregistrystatus) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------ | ------- | ------------------------ | +| `type` _string_ | Type is the storage type (configmap) | | Enum: [configmap]
| +| `configMapRef` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#localobjectreference-v1-core)_ | ConfigMapRef is a reference to a ConfigMap storage
Only used when Type is "configmap" | | Optional: \{\}
| + +#### api.v1alpha1.SyncPhase + +_Underlying type:_ _string_ + +SyncPhase represents the data synchronization state + +_Validation:_ + +- Enum: [Syncing Complete Failed] + +_Appears in:_ + +- [api.v1alpha1.SyncStatus](#apiv1alpha1syncstatus) + +| Field | Description | +| ---------- | ---------------------------------------------------------- | +| `Syncing` | SyncPhaseSyncing means sync is currently in progress
| +| `Complete` | SyncPhaseComplete means sync completed successfully
| +| `Failed` | SyncPhaseFailed means sync failed
| + +#### api.v1alpha1.SyncPolicy + +SyncPolicy defines automatic synchronization behavior. +When specified, enables automatic synchronization at the given interval. +Manual synchronization via annotation-based triggers is always available +regardless of this policy setting. + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryConfig](#apiv1alpha1mcpregistryconfig) + +| Field | Description | Default | Validation | +| ------------------- | ------------------------------------------------------------------------------------------------------------------ | ------- | -------------------------------------------------------------------------------------- | +| `interval` _string_ | Interval is the sync interval for automatic synchronization (Go duration format)
Examples: "1h", "30m", "24h" | | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Required: \{\}
| + +#### api.v1alpha1.SyncStatus + +SyncStatus provides detailed information about data synchronization + +_Appears in:_ + +- [api.v1alpha1.MCPRegistryStatus](#apiv1alpha1mcpregistrystatus) + +| Field | Description | Default | Validation | +| ---------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- | ------- | -------------------------------------- | +| `phase` _[api.v1alpha1.SyncPhase](#apiv1alpha1syncphase)_ | Phase represents the current synchronization phase | | Enum: [Syncing Complete Failed]
| +| `message` _string_ | Message provides additional information about the sync status | | Optional: \{\}
| +| `lastAttempt` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#time-v1-meta)_ | LastAttempt is the timestamp of the last sync attempt | | Optional: \{\}
| +| `attemptCount` _integer_ | AttemptCount is the number of sync attempts since last success | | Minimum: 0
Optional: \{\}
| +| `lastSyncTime` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#time-v1-meta)_ | LastSyncTime is the timestamp of the last successful sync | | Optional: \{\}
| +| `lastSyncHash` _string_ | LastSyncHash is the hash of the last successfully synced data
Used to detect changes in source data | | Optional: \{\}
| +| `serverCount` _integer_ | ServerCount is the total number of servers in the registry | | Minimum: 0
Optional: \{\}
| + +#### api.v1alpha1.TagFilter + +TagFilter defines tag-based filtering + +_Appears in:_ + +- [api.v1alpha1.RegistryFilter](#apiv1alpha1registryfilter) + +| Field | Description | Default | Validation | +| ------------------------ | ------------------------------------ | ------- | --------------------- | +| `include` _string array_ | Include is a list of tags to include | | Optional: \{\}
| +| `exclude` _string array_ | Exclude is a list of tags to exclude | | Optional: \{\}
| + +#### api.v1alpha1.TelemetryConfig + +TelemetryConfig defines observability configuration for the MCP server + +_Appears in:_ + +- [api.v1alpha1.MCPRemoteProxySpec](#apiv1alpha1mcpremoteproxyspec) +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------- | ---------------------------------------------------- | ------- | --------------------- | +| `openTelemetry` _[api.v1alpha1.OpenTelemetryConfig](#apiv1alpha1opentelemetryconfig)_ | OpenTelemetry defines OpenTelemetry configuration | | Optional: \{\}
| +| `prometheus` _[api.v1alpha1.PrometheusConfig](#apiv1alpha1prometheusconfig)_ | Prometheus defines Prometheus-specific configuration | | Optional: \{\}
| + +#### api.v1alpha1.TokenExchangeConfig + +TokenExchangeConfig holds configuration for RFC-8693 OAuth 2.0 Token Exchange. +This configuration is used to exchange incoming authentication tokens for tokens +that can be used with external services. +The structure matches the tokenexchange.Config from pkg/auth/tokenexchange/middleware.go + +_Appears in:_ + +- [api.v1alpha1.MCPExternalAuthConfigSpec](#apiv1alpha1mcpexternalauthconfigspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------- | +| `tokenUrl` _string_ | TokenURL is the OAuth 2.0 token endpoint URL for token exchange | | Required: \{\}
| +| `clientId` _string_ | ClientID is the OAuth 2.0 client identifier
Optional for some token exchange flows (e.g., Google Cloud Workforce Identity) | | Optional: \{\}
| +| `clientSecretRef` _[api.v1alpha1.SecretKeyRef](#apiv1alpha1secretkeyref)_ | ClientSecretRef is a reference to a secret containing the OAuth 2.0 client secret
Optional for some token exchange flows (e.g., Google Cloud Workforce Identity) | | Optional: \{\}
| +| `audience` _string_ | Audience is the target audience for the exchanged token | | Required: \{\}
| +| `scopes` _string array_ | Scopes is a list of OAuth 2.0 scopes to request for the exchanged token | | Optional: \{\}
| +| `subjectTokenType` _string_ | SubjectTokenType is the type of the incoming subject token.
Accepts short forms: "access_token" (default), "id_token", "jwt"
Or full URNs: "urn:ietf:params:oauth:token-type:access_token",
"urn:ietf:params:oauth:token-type:id_token",
"urn:ietf:params:oauth:token-type:jwt"
For Google Workload Identity Federation with OIDC providers (like Okta), use "id_token" | | Pattern: `^(access_token\|id_token\|jwt\|urn:ietf:params:oauth:token-type:(access_token\|id_token\|jwt))?$`
Optional: \{\}
| +| `externalTokenHeaderName` _string_ | ExternalTokenHeaderName is the name of the custom header to use for the exchanged token.
If set, the exchanged token will be added to this custom header (e.g., "X-Upstream-Token").
If empty or not set, the exchanged token will replace the Authorization header (default behavior). | | Optional: \{\}
| + +#### api.v1alpha1.TokenLifespanConfig + +TokenLifespanConfig holds configuration for token lifetimes. + +_Appears in:_ + +- [api.v1alpha1.EmbeddedAuthServerConfig](#apiv1alpha1embeddedauthserverconfig) + +| Field | Description | Default | Validation | +| ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------- | +| `accessTokenLifespan` _string_ | AccessTokenLifespan is the duration that access tokens are valid.
Format: Go duration string (e.g., "1h", "30m", "24h").
If empty, defaults to 1 hour. | | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Optional: \{\}
| +| `refreshTokenLifespan` _string_ | RefreshTokenLifespan is the duration that refresh tokens are valid.
Format: Go duration string (e.g., "168h", "7d" as "168h").
If empty, defaults to 7 days (168h). | | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Optional: \{\}
| +| `authCodeLifespan` _string_ | AuthCodeLifespan is the duration that authorization codes are valid.
Format: Go duration string (e.g., "10m", "5m").
If empty, defaults to 10 minutes. | | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Optional: \{\}
| + +#### api.v1alpha1.TokenResponseMapping + +TokenResponseMapping maps non-standard token response fields to standard OAuth 2.0 fields +using dot-notation JSON paths. This supports upstream providers like GovSlack that nest +the access token under paths like "authed_user.access_token". + +_Appears in:_ + +- [api.v1alpha1.OAuth2UpstreamConfig](#apiv1alpha1oauth2upstreamconfig) + +| Field | Description | Default | Validation | +| --------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------- | +| `accessTokenPath` _string_ | AccessTokenPath is the dot-notation path to the access token in the response.
Example: "authed_user.access_token" | | MinLength: 1
Required: \{\}
| +| `scopePath` _string_ | ScopePath is the dot-notation path to the scope string in the response.
If not specified, defaults to "scope". | | Optional: \{\}
| +| `refreshTokenPath` _string_ | RefreshTokenPath is the dot-notation path to the refresh token in the response.
If not specified, defaults to "refresh_token". | | Optional: \{\}
| +| `expiresInPath` _string_ | ExpiresInPath is the dot-notation path to the expires_in value (in seconds).
If not specified, defaults to "expires_in". | | Optional: \{\}
| + +#### api.v1alpha1.ToolAnnotationsOverride + +ToolAnnotationsOverride defines overrides for tool annotation fields. +All fields use pointers so nil means "don't override" while zero values +(empty string, false) mean "explicitly set to this value." + +_Appears in:_ + +- [api.v1alpha1.ToolOverride](#apiv1alpha1tooloverride) + +| Field | Description | Default | Validation | +| --------------------------- | ---------------------------------------------------------- | ------- | --------------------- | +| `title` _string_ | Title overrides the human-readable title annotation. | | Optional: \{\}
| +| `readOnlyHint` _boolean_ | ReadOnlyHint overrides the read-only hint annotation. | | Optional: \{\}
| +| `destructiveHint` _boolean_ | DestructiveHint overrides the destructive hint annotation. | | Optional: \{\}
| +| `idempotentHint` _boolean_ | IdempotentHint overrides the idempotent hint annotation. | | Optional: \{\}
| +| `openWorldHint` _boolean_ | OpenWorldHint overrides the open-world hint annotation. | | Optional: \{\}
| + +#### api.v1alpha1.ToolConfigRef + +ToolConfigRef defines a reference to a MCPToolConfig resource. +The referenced MCPToolConfig must be in the same namespace as the MCPServer. + +_Appears in:_ + +- [api.v1alpha1.MCPRemoteProxySpec](#apiv1alpha1mcpremoteproxyspec) +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) + +| Field | Description | Default | Validation | +| --------------- | -------------------------------------------------------------------- | ------- | --------------------- | +| `name` _string_ | Name is the name of the MCPToolConfig resource in the same namespace | | Required: \{\}
| + +#### api.v1alpha1.ToolOverride + +ToolOverride represents a tool override configuration. +Both Name and Description can be overridden independently, but +they can't be both empty. + +_Appears in:_ + +- [api.v1alpha1.MCPToolConfigSpec](#apiv1alpha1mcptoolconfigspec) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `name` _string_ | Name is the redefined name of the tool | | Optional: \{\}
| +| `description` _string_ | Description is the redefined description of the tool | | Optional: \{\}
| +| `annotations` _[api.v1alpha1.ToolAnnotationsOverride](#apiv1alpha1toolannotationsoverride)_ | Annotations overrides specific tool annotation fields.
Only specified fields are overridden; others pass through from the backend. | | Optional: \{\}
| + +#### api.v1alpha1.UpstreamProviderConfig + +UpstreamProviderConfig defines configuration for an upstream Identity Provider. + +_Appears in:_ + +- [api.v1alpha1.EmbeddedAuthServerConfig](#apiv1alpha1embeddedauthserverconfig) + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------- | +| `name` _string_ | Name uniquely identifies this upstream provider.
Used for routing decisions and session binding in multi-upstream scenarios.
Must be lowercase alphanumeric with hyphens (DNS-label-like). | | MaxLength: 63
MinLength: 1
Pattern: `^[a-z0-9]([a-z0-9-]*[a-z0-9])?$`
Required: \{\}
| +| `type` _[api.v1alpha1.UpstreamProviderType](#apiv1alpha1upstreamprovidertype)_ | Type specifies the provider type: "oidc" or "oauth2" | | Enum: [oidc oauth2]
Required: \{\}
| +| `oidcConfig` _[api.v1alpha1.OIDCUpstreamConfig](#apiv1alpha1oidcupstreamconfig)_ | OIDCConfig contains OIDC-specific configuration.
Required when Type is "oidc", must be nil when Type is "oauth2". | | Optional: \{\}
| +| `oauth2Config` _[api.v1alpha1.OAuth2UpstreamConfig](#apiv1alpha1oauth2upstreamconfig)_ | OAuth2Config contains OAuth 2.0-specific configuration.
Required when Type is "oauth2", must be nil when Type is "oidc". | | Optional: \{\}
| + +#### api.v1alpha1.UpstreamProviderType + +_Underlying type:_ _string_ + +UpstreamProviderType identifies the type of upstream Identity Provider. + +_Appears in:_ + +- [api.v1alpha1.UpstreamProviderConfig](#apiv1alpha1upstreamproviderconfig) + +| Field | Description | +| -------- | ---------------------------------------------------------------------------------------- | +| `oidc` | UpstreamProviderTypeOIDC is for OIDC providers with discovery support
| +| `oauth2` | UpstreamProviderTypeOAuth2 is for pure OAuth 2.0 providers with explicit endpoints
| + +#### api.v1alpha1.UserInfoConfig + +UserInfoConfig contains configuration for fetching user information from an upstream provider. +This supports both standard OIDC UserInfo endpoints and custom provider-specific endpoints +like GitHub's /user API. + +_Appears in:_ + +- [api.v1alpha1.OAuth2UpstreamConfig](#apiv1alpha1oauth2upstreamconfig) +- [api.v1alpha1.OIDCUpstreamConfig](#apiv1alpha1oidcupstreamconfig) + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ---------------------------------------------------- | +| `endpointUrl` _string_ | EndpointURL is the URL of the userinfo endpoint. | | Pattern: `^https?://.*$`
Required: \{\}
| +| `httpMethod` _string_ | HTTPMethod is the HTTP method to use for the userinfo request.
If not specified, defaults to GET. | | Enum: [GET POST]
Optional: \{\}
| +| `additionalHeaders` _object (keys:string, values:string)_ | AdditionalHeaders contains extra headers to include in the userinfo request.
Useful for providers that require specific headers (e.g., GitHub's Accept header). | | Optional: \{\}
| +| `fieldMapping` _[api.v1alpha1.UserInfoFieldMapping](#apiv1alpha1userinfofieldmapping)_ | FieldMapping contains custom field mapping configuration for non-standard providers.
If nil, standard OIDC field names are used ("sub", "name", "email"). | | Optional: \{\}
| + +#### api.v1alpha1.UserInfoFieldMapping + +_Underlying type:_ _[api.v1alpha1.struct{SubjectFields []string "json:\"subjectFields,omitempty\""; NameFields []string "json:\"nameFields,omitempty\""; EmailFields []string "json:\"emailFields,omitempty\""}](#apiv1alpha1struct{subjectfields []string "json:\"subjectfields,omitempty\""; namefields []string "json:\"namefields,omitempty\""; emailfields []string "json:\"emailfields,omitempty\""})_ + +UserInfoFieldMapping maps provider-specific field names to standard UserInfo fields. +This allows adapting non-standard provider responses to the canonical UserInfo structure. +Each field supports an ordered list of claim names to try. The first non-empty value +found will be used. + +Example for GitHub: + + fieldMapping: + subjectFields: ["id", "login"] + nameFields: ["name", "login"] + emailFields: ["email"] + +_Appears in:_ + +- [api.v1alpha1.UserInfoConfig](#apiv1alpha1userinfoconfig) + +#### api.v1alpha1.ValidationStatus + +_Underlying type:_ _string_ + +ValidationStatus represents the validation state of a workflow + +_Validation:_ + +- Enum: [Valid Invalid Unknown] + +_Appears in:_ + +- [api.v1alpha1.VirtualMCPCompositeToolDefinitionStatus](#apiv1alpha1virtualmcpcompositetooldefinitionstatus) + +| Field | Description | +| --------- | ---------------------------------------------------------------------------- | +| `Valid` | ValidationStatusValid indicates the workflow is valid
| +| `Invalid` | ValidationStatusInvalid indicates the workflow has validation errors
| +| `Unknown` | ValidationStatusUnknown indicates validation hasn't been performed yet
| + +#### api.v1alpha1.VirtualMCPCompositeToolDefinition + +VirtualMCPCompositeToolDefinition is the Schema for the virtualmcpcompositetooldefinitions API +VirtualMCPCompositeToolDefinition defines reusable composite workflows that can be referenced +by multiple VirtualMCPServer instances + +_Appears in:_ + +- [api.v1alpha1.VirtualMCPCompositeToolDefinitionList](#apiv1alpha1virtualmcpcompositetooldefinitionlist) + +| Field | Description | Default | Validation | +| ---------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `VirtualMCPCompositeToolDefinition` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `spec` _[api.v1alpha1.VirtualMCPCompositeToolDefinitionSpec](#apiv1alpha1virtualmcpcompositetooldefinitionspec)_ | | | | +| `status` _[api.v1alpha1.VirtualMCPCompositeToolDefinitionStatus](#apiv1alpha1virtualmcpcompositetooldefinitionstatus)_ | | | | + +#### api.v1alpha1.VirtualMCPCompositeToolDefinitionList + +VirtualMCPCompositeToolDefinitionList contains a list of VirtualMCPCompositeToolDefinition + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `VirtualMCPCompositeToolDefinitionList` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `items` _[api.v1alpha1.VirtualMCPCompositeToolDefinition](#apiv1alpha1virtualmcpcompositetooldefinition) array_ | | | | + +#### api.v1alpha1.VirtualMCPCompositeToolDefinitionSpec + +VirtualMCPCompositeToolDefinitionSpec defines the desired state of VirtualMCPCompositeToolDefinition. +This embeds the CompositeToolConfig from pkg/vmcp/config to share the configuration model +between CLI and operator usage. + +_Appears in:_ + +- [api.v1alpha1.VirtualMCPCompositeToolDefinition](#apiv1alpha1virtualmcpcompositetooldefinition) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | ------------------------------------------------------------------------------------ | +| `name` _string_ | Name is the workflow name (unique identifier). | | | +| `description` _string_ | Description describes what the workflow does. | | | +| `parameters` _[pkg.json.Map](#pkgjsonmap)_ | Parameters defines input parameter schema in JSON Schema format.
Should be a JSON Schema object with "type": "object" and "properties".
Example:
\{
"type": "object",
"properties": \{
"param1": \{"type": "string", "default": "value"\},
"param2": \{"type": "integer"\}
\},
"required": ["param2"]
\}
We use json.Map rather than a typed struct because JSON Schema is highly
flexible with many optional fields (default, enum, minimum, maximum, pattern,
items, additionalProperties, oneOf, anyOf, allOf, etc.). Using json.Map
allows full JSON Schema compatibility without needing to define every possible
field, and matches how the MCP SDK handles inputSchema. | | Optional: \{\}
| +| `timeout` _[vmcp.config.Duration](#vmcpconfigduration)_ | Timeout is the maximum workflow execution time. | | Pattern: `^([0-9]+(\.[0-9]+)?(ns\|us\|µs\|ms\|s\|m\|h))+$`
Type: string
| +| `steps` _[vmcp.config.WorkflowStepConfig](#vmcpconfigworkflowstepconfig) array_ | Steps are the workflow steps to execute. | | | +| `output` _[vmcp.config.OutputConfig](#vmcpconfigoutputconfig)_ | Output defines the structured output schema for this workflow.
If not specified, the workflow returns the last step's output (backward compatible). | | Optional: \{\}
| + +#### api.v1alpha1.VirtualMCPCompositeToolDefinitionStatus + +VirtualMCPCompositeToolDefinitionStatus defines the observed state of VirtualMCPCompositeToolDefinition + +_Appears in:_ + +- [api.v1alpha1.VirtualMCPCompositeToolDefinition](#apiv1alpha1virtualmcpcompositetooldefinition) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------------------------------------------- | +| `validationStatus` _[api.v1alpha1.ValidationStatus](#apiv1alpha1validationstatus)_ | ValidationStatus indicates the validation state of the workflow
- Valid: Workflow structure is valid
- Invalid: Workflow has validation errors | | Enum: [Valid Invalid Unknown]
Optional: \{\}
| +| `validationErrors` _string array_ | ValidationErrors contains validation error messages if ValidationStatus is Invalid | | Optional: \{\}
| +| `referencingVirtualServers` _string array_ | ReferencingVirtualServers lists VirtualMCPServer resources that reference this workflow
This helps track which servers need to be reconciled when this workflow changes | | Optional: \{\}
| +| `observedGeneration` _integer_ | ObservedGeneration is the most recent generation observed for this VirtualMCPCompositeToolDefinition
It corresponds to the resource's generation, which is updated on mutation by the API Server | | Optional: \{\}
| +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#condition-v1-meta) array_ | Conditions represent the latest available observations of the workflow's state | | Optional: \{\}
| + +#### api.v1alpha1.VirtualMCPServer + +VirtualMCPServer is the Schema for the virtualmcpservers API +VirtualMCPServer aggregates multiple backend MCPServers into a unified endpoint + +_Appears in:_ + +- [api.v1alpha1.VirtualMCPServerList](#apiv1alpha1virtualmcpserverlist) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `VirtualMCPServer` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `spec` _[api.v1alpha1.VirtualMCPServerSpec](#apiv1alpha1virtualmcpserverspec)_ | | | | +| `status` _[api.v1alpha1.VirtualMCPServerStatus](#apiv1alpha1virtualmcpserverstatus)_ | | | | + +#### api.v1alpha1.VirtualMCPServerList + +VirtualMCPServerList contains a list of VirtualMCPServer + +| Field | Description | Default | Validation | +| -------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- | --------------------- | +| `apiVersion` _string_ | `toolhive.stacklok.dev/v1alpha1` | | | +| `kind` _string_ | `VirtualMCPServerList` | | | +| `kind` _string_ | Kind is a string value representing the REST resource this object represents.
Servers may infer this from the endpoint the client submits requests to.
Cannot be updated.
In CamelCase.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds | | Optional: \{\}
| +| `apiVersion` _string_ | APIVersion defines the versioned schema of this representation of an object.
Servers should convert recognized schemas to the latest internal value, and
may reject unrecognized values.
More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources | | Optional: \{\}
| +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | | +| `items` _[api.v1alpha1.VirtualMCPServer](#apiv1alpha1virtualmcpserver) array_ | | | | + +#### api.v1alpha1.VirtualMCPServerPhase + +_Underlying type:_ _string_ + +VirtualMCPServerPhase represents the lifecycle phase of a VirtualMCPServer + +_Validation:_ + +- Enum: [Pending Ready Degraded Failed] + +_Appears in:_ + +- [api.v1alpha1.VirtualMCPServerStatus](#apiv1alpha1virtualmcpserverstatus) + +| Field | Description | +| ---------- | --------------------------------------------------------------------------------------------------------------- | +| `Pending` | VirtualMCPServerPhasePending indicates the VirtualMCPServer is being initialized
| +| `Ready` | VirtualMCPServerPhaseReady indicates the VirtualMCPServer is ready and serving requests
| +| `Degraded` | VirtualMCPServerPhaseDegraded indicates the VirtualMCPServer is running but some backends are unavailable
| +| `Failed` | VirtualMCPServerPhaseFailed indicates the VirtualMCPServer has failed
| + +#### api.v1alpha1.VirtualMCPServerSpec + +VirtualMCPServerSpec defines the desired state of VirtualMCPServer + +_Appears in:_ + +- [api.v1alpha1.VirtualMCPServer](#apiv1alpha1virtualmcpserver) + +| Field | Description | Default | Validation | +| --------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | --------- | ------------------------------------------------------------------- | +| `incomingAuth` _[api.v1alpha1.IncomingAuthConfig](#apiv1alpha1incomingauthconfig)_ | IncomingAuth configures authentication for clients connecting to the Virtual MCP server.
Must be explicitly set - use "anonymous" type when no authentication is required.
This field takes precedence over config.IncomingAuth and should be preferred because it
supports Kubernetes-native secret references (SecretKeyRef, ConfigMapRef) for secure
dynamic discovery of credentials, rather than requiring secrets to be embedded in config. | | Required: \{\}
| +| `outgoingAuth` _[api.v1alpha1.OutgoingAuthConfig](#apiv1alpha1outgoingauthconfig)_ | OutgoingAuth configures authentication from Virtual MCP to backend MCPServers.
This field takes precedence over config.OutgoingAuth and should be preferred because it
supports Kubernetes-native secret references (SecretKeyRef, ConfigMapRef) for secure
dynamic discovery of credentials, rather than requiring secrets to be embedded in config. | | Optional: \{\}
| +| `serviceType` _string_ | ServiceType specifies the Kubernetes service type for the Virtual MCP server | ClusterIP | Enum: [ClusterIP NodePort LoadBalancer]
Optional: \{\}
| +| `sessionAffinity` _string_ | SessionAffinity controls whether the Service routes repeated client connections to the same pod.
MCP protocols (SSE, streamable-http) are stateful, so ClientIP is the default.
Set to "None" for stateless servers or when using an external load balancer with its own affinity. | ClientIP | Enum: [ClientIP None]
Optional: \{\}
| +| `serviceAccount` _string_ | ServiceAccount is the name of an already existing service account to use by the Virtual MCP server.
If not specified, a ServiceAccount will be created automatically and used by the Virtual MCP server. | | Optional: \{\}
| +| `podTemplateSpec` _[RawExtension](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#rawextension-runtime-pkg)_ | PodTemplateSpec defines the pod template to use for the Virtual MCP server
This allows for customizing the pod configuration beyond what is provided by the other fields.
Note that to modify the specific container the Virtual MCP server runs in, you must specify
the 'vmcp' container name in the PodTemplateSpec.
This field accepts a PodTemplateSpec object as JSON/YAML. | | Type: object
Optional: \{\}
| +| `config` _[vmcp.config.Config](#vmcpconfigconfig)_ | Config is the Virtual MCP server configuration
The only field currently required within config is `config.groupRef`.
GroupRef references an existing MCPGroup that defines backend workloads.
The referenced MCPGroup must exist in the same namespace.
The telemetry and audit config from here are also supported, but not required. | | Type: object
Optional: \{\}
| +| `embeddingServerRef` _[api.v1alpha1.EmbeddingServerRef](#apiv1alpha1embeddingserverref)_ | EmbeddingServerRef references an existing EmbeddingServer resource by name.
When the optimizer is enabled, this field is required to point to a ready EmbeddingServer
that provides embedding capabilities.
The referenced EmbeddingServer must exist in the same namespace and be ready. | | Optional: \{\}
| +| `authServerConfig` _[api.v1alpha1.EmbeddedAuthServerConfig](#apiv1alpha1embeddedauthserverconfig)_ | AuthServerConfig configures an embedded OAuth authorization server.
When set, the vMCP server acts as an OIDC issuer, drives users through
upstream IDPs, and issues ToolHive JWTs. The embedded AS becomes the
IncomingAuth OIDC provider — its issuer must match IncomingAuth.OIDCConfig
so that tokens it issues are accepted by the vMCP's incoming auth middleware.
When nil, IncomingAuth uses an external IDP and behavior is unchanged. | | Optional: \{\}
| +| `replicas` _integer_ | Replicas is the desired number of vMCP pod replicas.
VirtualMCPServer creates a single Deployment for the vMCP aggregator process,
so there is only one replicas field (unlike MCPServer which has separate
Replicas and BackendReplicas for its two Deployments).
When nil, the operator does not set Deployment.Spec.Replicas, leaving replica
management to an HPA or other external controller. | | Minimum: 0
Optional: \{\}
| +| `sessionStorage` _[api.v1alpha1.SessionStorageConfig](#apiv1alpha1sessionstorageconfig)_ | SessionStorage configures session storage for stateful horizontal scaling.
When nil, no session storage is configured. | | Optional: \{\}
| + +#### api.v1alpha1.VirtualMCPServerStatus + +VirtualMCPServerStatus defines the observed state of VirtualMCPServer + +_Appears in:_ + +- [api.v1alpha1.VirtualMCPServer](#apiv1alpha1virtualmcpserver) + +| Field | Description | Default | Validation | +| ------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------- | ------- | ----------------------------------------------------------------- | +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.27/#condition-v1-meta) array_ | Conditions represent the latest available observations of the VirtualMCPServer's state | | Optional: \{\}
| +| `observedGeneration` _integer_ | ObservedGeneration is the most recent generation observed for this VirtualMCPServer | | Optional: \{\}
| +| `phase` _[api.v1alpha1.VirtualMCPServerPhase](#apiv1alpha1virtualmcpserverphase)_ | Phase is the current phase of the VirtualMCPServer | Pending | Enum: [Pending Ready Degraded Failed]
Optional: \{\}
| +| `message` _string_ | Message provides additional information about the current phase | | Optional: \{\}
| +| `url` _string_ | URL is the URL where the Virtual MCP server can be accessed | | Optional: \{\}
| +| `discoveredBackends` _[api.v1alpha1.DiscoveredBackend](#apiv1alpha1discoveredbackend) array_ | DiscoveredBackends lists discovered backend configurations from the MCPGroup | | Optional: \{\}
| +| `backendCount` _integer_ | BackendCount is the number of healthy/ready backends
(excludes unavailable, degraded, and unknown backends) | | Optional: \{\}
| + +#### api.v1alpha1.Volume + +Volume represents a volume to mount in a container + +_Appears in:_ + +- [api.v1alpha1.MCPServerSpec](#apiv1alpha1mcpserverspec) + +| Field | Description | Default | Validation | +| -------------------- | ----------------------------------------------------------------- | ------- | --------------------- | +| `name` _string_ | Name is the name of the volume | | Required: \{\}
| +| `hostPath` _string_ | HostPath is the path on the host to mount | | Required: \{\}
| +| `mountPath` _string_ | MountPath is the path in the container to mount to | | Required: \{\}
| +| `readOnly` _boolean_ | ReadOnly specifies whether the volume should be mounted read-only | false | Optional: \{\}
| diff --git a/versioned_docs/version-1.1/toolhive/reference/index.mdx b/versioned_docs/version-1.1/toolhive/reference/index.mdx new file mode 100644 index 00000000..15c72cb5 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/index.mdx @@ -0,0 +1,79 @@ +--- +title: Reference +description: + Technical reference documentation for ToolHive, including CLI commands, API + specifications, CRD definitions, and registry schemas. +displayed_sidebar: toolhiveSidebar +--- + +import DocCard from '@theme/DocCard'; + +Technical reference documentation for all ToolHive components. For guides and +tutorials, see the individual product sections. + +
+ + + + + + + + + + + + + + + +
diff --git a/versioned_docs/version-1.1/toolhive/reference/registry-api.mdx b/versioned_docs/version-1.1/toolhive/reference/registry-api.mdx new file mode 100644 index 00000000..f40ae7be --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/registry-api.mdx @@ -0,0 +1,10 @@ +--- +title: Registry Server API reference +sidebar_label: API reference +description: Reference documentation for the ToolHive Registry Server REST API +hide_table_of_contents: true +--- + +import ApiDocMdx from '@theme/ApiDocMdx'; + + diff --git a/versioned_docs/version-1.1/toolhive/reference/registry-schema-toolhive.mdx b/versioned_docs/version-1.1/toolhive/reference/registry-schema-toolhive.mdx new file mode 100644 index 00000000..d955e5e6 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/registry-schema-toolhive.mdx @@ -0,0 +1,34 @@ +--- +title: ToolHive registry JSON schema +description: The JSON schema for the ToolHive registry. +displayed_sidebar: toolhiveSidebar +--- + +import JSONSchemaViewer from '@theme/JSONSchemaViewer'; +import Schema from '@site/static/api-specs/toolhive-legacy-registry.schema.json'; + +This is the JSON schema for the ToolHive-native registry format. It defines the +structure and constraints for registry entries, ensuring that all entries +conform to a consistent format. + +:::info + +This format is considered legacy now that an +[official upstream registry format](registry-schema-upstream.mdx) exists. +ToolHive supports both formats, and the ToolHive-native format continues to work +for the built-in registry, custom file-based registries, and the ToolHive +Registry Server. + +::: + +To use this schema in your own custom registry file, add a `$schema` property at +the top of your JSON file: + +```json +{ + "$schema": "https://raw.githubusercontent.com/stacklok/toolhive-core/main/registry/types/data/toolhive-legacy-registry.schema.json", + ... +} +``` + + diff --git a/versioned_docs/version-1.1/toolhive/reference/registry-schema-upstream.mdx b/versioned_docs/version-1.1/toolhive/reference/registry-schema-upstream.mdx new file mode 100644 index 00000000..c9ba8314 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/reference/registry-schema-upstream.mdx @@ -0,0 +1,205 @@ +--- +title: Upstream registry JSON schema +description: The JSON schema for the official upstream MCP registry format. +displayed_sidebar: toolhiveSidebar +--- + +import JSONSchemaViewer from '@theme/JSONSchemaViewer'; +import RegistrySchema from '@site/static/api-specs/upstream-registry.schema.json'; +import McpServerSchema from '@site/static/api-specs/mcp-server.schema.json'; +import PublisherProvidedSchema from '@site/static/api-specs/publisher-provided.schema.json'; + +This is the JSON schema for the official upstream MCP registry format. It +defines the structure and constraints for registry entries, ensuring that all +entries conform to a consistent format. The registry wraps the official MCP +server schema, which is documented separately below. + +:::info + +ToolHive also supports the +[ToolHive-native registry format](./registry-schema-toolhive.mdx), which is used +by the built-in registry. Both formats work with custom file-based registries +and the ToolHive Registry Server. + +::: + +To use this schema in your own custom registry file, add a `$schema` property at +the top of your JSON file: + +```json +{ + "$schema": "https://raw.githubusercontent.com/stacklok/toolhive-core/main/registry/types/data/upstream-registry.schema.json", + ... +} +``` + +## Registry schema + + + +## MCP server object schema + +The `servers` array in the registry contains objects that conform to the +official MCP server schema: + + + +## ToolHive extensions schema + +ToolHive extends the upstream MCP server schema with additional metadata to +support features like container configuration, authentication, and +categorization. These extensions are stored in the +`_meta['io.modelcontextprotocol.registry/publisher-provided']` property of each +server definition. + +The available properties vary by server type: + +- **Container servers** (keyed by OCI image reference): `permissions`, `args`, + `provenance`, `docker_tags`, and `proxy_port` +- **Remote servers** (keyed by URL): `oauth_config` and `env_vars` +- **All servers**: `status`, `tier`, `tools`, `tags`, `metadata`, and + `custom_metadata` + +:::note Read-only metadata + +The `metadata` object can include an optional `kubernetes` nested object with +fields like `kind`, `namespace`, `name`, `uid`, `image`, and `transport`. This +metadata is automatically added by the ToolHive Registry Server for servers that +were auto-discovered from Kubernetes deployments (typically remote servers +accessed via Kubernetes-exposed URLs). You should not manually add this field +when creating custom registry files. + +::: + + + +## Full example + +The following example shows a complete custom registry file with both a +container server and a remote server, demonstrating the different ToolHive +extensions you can use when building your own registry: + +```json title="Example registry file with ToolHive extensions" +{ + "$schema": "https://raw.githubusercontent.com/stacklok/toolhive-core/main/registry/types/data/upstream-registry.schema.json", + "version": "1.0.0", + "meta": { + "last_updated": "2026-02-05T16:59:49Z" + }, + "data": { + "servers": [ + { + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", + "name": "io.github.stacklok/osv", + "description": "OSV (Open Source Vulnerabilities) database access for querying package and commit vulnerabilities", + "title": "osv", + "repository": { + "url": "https://github.com/StacklokLabs/osv-mcp", + "source": "github" + }, + "version": "1.0.0", + // highlight-next-line + "packages": [ + { + "registryType": "oci", + "identifier": "ghcr.io/stackloklabs/osv-mcp/server:0.1.0", + "transport": { + "type": "streamable-http", + "url": "http://localhost:8080" + } + } + ], + // highlight-start + "_meta": { + "io.modelcontextprotocol.registry/publisher-provided": { + "io.github.stacklok": { + "ghcr.io/stackloklabs/osv-mcp/server:0.1.0": { + // highlight-end + "metadata": { + "last_updated": "2026-01-30T02:55:46Z", + "pulls": 0, + "stars": 0 + }, + "permissions": { + "network": { + "outbound": { + "allow_host": ["api.osv.dev"], + "allow_port": [443] + } + } + }, + "provenance": { + "sigstore_url": "tuf-repo-cdn.sigstore.dev", + "repository_uri": "https://github.com/StacklokLabs/osv-mcp", + "signer_identity": "/.github/workflows/release.yml", + "runner_environment": "github-hosted", + "cert_issuer": "https://token.actions.githubusercontent.com" + }, + "status": "Active", + "tags": ["vulnerability", "security", "osv"], + "tier": "Community", + "tools": [ + "query_vulnerability", + "query_vulnerabilities_batch", + "get_vulnerability" + ] + } + } + } + } + }, + { + "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json", + "name": "io.github.stacklok/toolhive-doc-mcp-remote", + "description": "Search ToolHive docs for help with using and contributing to the project (hosted version)", + "title": "toolhive-doc-mcp-remote", + "repository": { + "url": "https://github.com/StacklokLabs/toolhive-doc-mcp", + "source": "github" + }, + "version": "1.0.0", + // highlight-next-line + "remotes": [ + { + "type": "streamable-http", + "url": "https://toolhive-doc-mcp.stacklok.dev/mcp" + } + ], + // highlight-start + "_meta": { + "io.modelcontextprotocol.registry/publisher-provided": { + "io.github.stacklok": { + "https://toolhive-doc-mcp.stacklok.dev/mcp": { + // highlight-end + "custom_metadata": { + "author": "Stacklok", + "homepage": "https://github.com/StacklokLabs/toolhive-doc-mcp", + "license": "Apache-2.0" + }, + "metadata": { + "last_updated": "2026-01-25T13:39:45Z", + "pulls": 0, + "stars": 3 + }, + "status": "Active", + "tags": [ + "remote", + "docs", + "documentation", + "help", + "search", + "stacklok", + "support", + "toolhive" + ], + "tier": "Official", + "tools": ["query_docs", "get_chunk"] + } + } + } + } + } + ] + } +} +``` diff --git a/versioned_docs/version-1.1/toolhive/support.mdx b/versioned_docs/version-1.1/toolhive/support.mdx new file mode 100644 index 00000000..31c477cc --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/support.mdx @@ -0,0 +1,117 @@ +--- +title: Getting support +sidebar_label: Support +description: How to get help and support for ToolHive +--- + +Whether you're troubleshooting an issue, looking for guidance, or need help with +your ToolHive deployment, there are several ways to get support. This page +outlines the available resources to help you find answers quickly. + +## Self-help resources + +### Documentation + +This documentation site is your first stop for help. You can search using +keywords or ask questions using AI-powered search: + +- **Keyword search**: Use the search bar at the top of the page to find specific + topics, commands, or configuration options. +- **AI search**: Type a question in the search and select the **Ask AI** option + to get relevant answers from the documentation. + +### ToolHive documentation MCP server + +For an enhanced documentation experience, you can use the ToolHive documentation +search MCP server. This server provides access to the ToolHive documentation +directly within your AI-powered tools, making it easy to get context-aware help +while you work. + +ToolHive offers two ways to access the documentation server: + +- **`toolhive-doc-mcp-remote`** (recommended): A hosted remote MCP server that's + always up-to-date with the latest documentation. +- **`toolhive-doc-mcp`**: A containerized local version you can run on your + machine. + +Both versions are available in the ToolHive registry. The +[`toolhive-doc-mcp`](https://github.com/StacklokLabs/toolhive-doc-mcp) source +code is available on GitHub. + +:::tip + +Once you've added the ToolHive documentation MCP server, you can ask your AI +assistant questions like "How do I configure ToolHive?" or "What are the +available commands?" and get answers directly from the documentation. + +::: + +#### Using with the ToolHive UI + +1. Open the ToolHive UI application. +2. Open the **Registry** page. +3. Search for `toolhive-doc` to find both versions. +4. Select `toolhive-doc-mcp-remote` (recommended) or `toolhive-doc-mcp` (local). +5. Review the pre-filled configuration and click **Install server** to start the + MCP server. + +The server will appear on the MCP Servers page with a "Running" status. The +documentation is now available to your connected AI clients. + +See the [Run MCP servers](./guides-ui/run-mcp-servers.mdx) guide for more +details. + +#### Using with the ToolHive CLI + +To run the hosted remote version (recommended): + +```bash +thv run toolhive-doc-mcp-remote +``` + +Or to run the local containerized version: + +```bash +thv run toolhive-doc-mcp +``` + +Once started, the documentation will be available to any AI clients registered +with ToolHive. To verify the server is running: + +```bash +thv list +``` + +See the [Run MCP servers](./guides-cli/run-mcp-servers.mdx) guide for more +details. + +## Community support + +### Discord + +Join the [Stacklok Discord community](https://discord.gg/stacklok) to connect +with the ToolHive community. The `#user-chat` channel is the place to ask usage +questions, share tips, and get help from other users and the Stacklok team. + +You can also ask questions to the `@stacklok-bot` chatbot in Discord. The bot +can help answer common questions and provide guidance on using ToolHive. + +### GitHub issues + +If you've found a bug or want to request a new feature, please open an issue on +the [ToolHive GitHub repository](https://github.com/stacklok/toolhive). When +reporting a bug, include as much detail as possible, such as: + +- Your operating system and version +- The version of ToolHive you're using +- Steps to reproduce the issue +- Any error messages or logs + +This information helps the team diagnose and fix issues more quickly. + +## Enterprise support + +For organizations that need an enterprise distribution of ToolHive with +dedicated support, custom integrations, or assistance with large-scale +deployments, see the [Stacklok Enterprise](./enterprise.mdx) page to learn more +and get in touch. diff --git a/versioned_docs/version-1.1/toolhive/tutorials/custom-registry.mdx b/versioned_docs/version-1.1/toolhive/tutorials/custom-registry.mdx new file mode 100644 index 00000000..ac38b280 --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/tutorials/custom-registry.mdx @@ -0,0 +1,375 @@ +--- +title: Create a custom MCP registry +description: Learn how to create a custom MCP registry for ToolHive. +--- + +## Overview + +ToolHive includes a built-in registry of MCP servers with verified +configurations that meet a +[minimum quality standard](../concepts/registry-criteria.mdx). + +But you can also create your own custom registry to include the MCP servers that +are relevant to your organization or specific use cases. This allows you to +curate a list of servers that meet your specific needs. + +## Why create a custom registry? + +Creating a custom registry allows you to: + +- Curate a list of MCP servers tailored to your organization's needs +- Include private or internal servers not listed in the public registry +- Pre-configure server settings for easier deployment +- Ensure all servers meet your organization's quality and security standards + +## Create your first custom registry + +In this tutorial, you'll create a custom MCP registry for ToolHive and configure +it to use your own curated list of MCP servers. By the end, you'll have a +working custom registry that you can extend with your organization's specific +MCP servers. + +### What you'll build + +You'll create a JSON registry file containing a simple MCP server entry, +configure ToolHive to use your custom registry, and verify it works by listing +and running servers from your registry. + +### Prerequisites + +Before you start, make sure you have: + +- The ToolHive UI or CLI installed and working on your system + - [ToolHive UI quickstart](../guides-ui/quickstart.mdx) + - [ToolHive CLI quickstart](../guides-cli/quickstart.mdx) +- Basic familiarity with JSON format +- A text editor for creating the registry file + +### Step 1: Create the registry file + +First, create a new directory for your custom registry and navigate to it: + +```bash +mkdir my-custom-registry +cd my-custom-registry +``` + +Create a new file called `registry.json` and add the following content: + +```json title='registry.json' +{ + "$schema": "https://raw.githubusercontent.com/stacklok/toolhive-core/main/registry/types/data/toolhive-legacy-registry.schema.json", + "version": "1.0.0", + "last_updated": "2025-08-15T10:00:00Z", + "servers": { + "my-fetch": { + "description": "A custom web content fetching MCP server for our organization", + "image": "ghcr.io/stackloklabs/gofetch/server:latest", + "status": "Active", + "tier": "Community", + "transport": "streamable-http", + "args": ["--user-agent", "Mozilla/5.0 (compatible;MyOrgFetchBot/1.0)"], + "tags": ["web", "fetch", "content"], + "tools": ["fetch"], + "permissions": { + "network": { + "outbound": { + "allow_host": [".example.com", "api.mycompany.com"], + "allow_port": [80, 443] + } + } + } + } + }, + "groups": [ + { + "name": "dev-toolkit", + "description": "Essential MCP servers for development workflows", + "servers": { + "fetch-tool": { + "description": "A web content fetching MCP server for development", + "image": "ghcr.io/stackloklabs/gofetch/server:latest", + "status": "Active", + "tier": "Community", + "transport": "streamable-http", + "args": ["--user-agent", "Mozilla/5.0 (compatible;DevBot/1.0)"], + "tags": ["web", "fetch", "dev"], + "tools": ["fetch"], + "permissions": { + "network": { + "outbound": { + "allow_host": [".github.com", ".stackoverflow.com"], + "allow_port": [80, 443] + } + } + } + } + } + } + ] +} +``` + +This registry contains two types of server configurations: + +- A standalone MCP server called `my-fetch` in the top-level `servers` section +- A group called `dev-toolkit` containing a `fetch-tool` server + +The `dev-toolkit` group demonstrates how you can organize related servers +together. When you run this group later, you'll be able to start multiple +servers as a single unit—perfect for development workflows or team-specific +toolsets. Notice how the `fetch-tool` server has a different user agent and +network permissions than `my-fetch`, showing how you can customize servers +within groups for specific purposes. + +Your server configurations include everything ToolHive needs to run them: + +- A description explaining its purpose +- The container image to use +- Required metadata like status and tier +- A list of tools it provides +- Command-line arguments for customization +- Network permissions for security + +:::note + +Registry groups are supported in both the CLI and the ToolHive UI. + +::: + +### Step 2: Configure ToolHive to use your registry + +Configure ToolHive to use your custom registry. + + + + +1. Open the ToolHive application +2. Navigate to **Settings** → **Registry** +3. Select the **Local Registry** option +4. Enter the full path to your `registry.json` file (for example: + `/Users//my-custom-registry/registry.json`) +5. Click **Save** to apply the configuration + +The UI will validate the registry file and confirm it's been set successfully. + + + + +Set the registry file using the full path: + +```bash +thv config set-registry /Users//my-custom-registry/registry.json +``` + +Verify the configuration was applied: + +```bash +thv config get-registry +``` + +You should see the path to your registry file displayed. + + + + +### Step 3: Test your custom registry + +Verify your custom registry is working. + + + + +1. Navigate to the **Registry** page from the menu bar +2. You should see your `my-fetch` server displayed alongside any other servers + in your custom registry +3. Click on the server to view its details, including description, tools, and + configuration options + + + + +List the servers in your custom registry: + +```bash +thv registry list +``` + +You should see your `my-fetch` server listed. Get detailed information about it: + +```bash +thv registry info my-fetch +``` + +This displays the server's configuration, tools, and permissions as defined in +your registry. + + + + +### Step 4: Run a server from your registry + +Finally, run the MCP server from your custom registry. + + + + +1. On the **Registry** page, click on your `my-fetch` server +2. Click the **Install server** button +3. Configure any required settings (the defaults from your registry will be + pre-populated) +4. Click **Install server** to start the MCP server +5. Navigate to the **MCP Servers** page to see your running server and manage it + +The server will appear in your MCP servers list, where you can start, stop, view +logs, and manage it like any other MCP server. + +To install the `dev-toolkit` group, click on it in the registry grid and then +click **Install group**. The installation wizard guides you through configuring +each server in the group one at a time. For more details on installing groups, +see [Explore the registry](../guides-ui/registry.mdx#registry-groups). + + + + +```bash +thv run my-fetch +``` + +The server should start successfully, demonstrating that your custom registry is +working correctly. + +Next, run the `dev-toolkit` group: + +```bash +thv group run dev-toolkit +``` + +This starts all servers in the `dev-toolkit` group (in this case, just the +`fetch-tool` server). + + + + +### Step 5: Add more servers (optional) + +You can extend your registry by adding more servers to the `servers` object. For +example, add a second server (note this is just an example, it will not function +if you try to run it): + +```json title='registry.json' +{ + "$schema": "https://raw.githubusercontent.com/stacklok/toolhive-core/main/registry/types/data/toolhive-legacy-registry.schema.json", + "version": "1.0.0", + "last_updated": "2025-08-15T10:00:00Z", + "servers": { + "my-fetch": { + // ... existing server configuration + }, + "company-tools": { + "description": "Internal company tools and utilities MCP server", + "image": "registry.company.com/mcp/company-tools:v1.2.0", + "status": "Active", + "tier": "Community", + "transport": "stdio", + "tools": ["get_employee_info", "create_ticket", "check_inventory"], + "tags": ["internal", "company", "tools"], + "env_vars": [ + { + "name": "COMPANY_API_KEY", + "description": "API key for accessing company internal services", + "required": true, + "secret": true + } + ] + } + } +} +``` + +After updating the file, the new server is immediately available for ToolHive +CLI commands. For the UI, navigate to the registry settings and click **Save** +to see the new server listed. + +## Production considerations + +While this tutorial uses a local file for simplicity, in production environments +you should: + +- **Host your registry on a secure HTTP server** +- **Use version control** to track changes to your registry +- **Implement proper access controls** to prevent unauthorized modifications +- **Set up automated validation** to ensure registry entries follow the schema +- **Regularly update** the registry with new servers and remove deprecated ones + +For production use, configure ToolHive to use a remote registry URL: + +```bash +thv config set-registry https://registry.example.com/mcp-registry.json +``` + +## What you've learned + +You've successfully: + +- Created a custom MCP registry following the JSON schema +- Configured ToolHive to use your custom registry +- Added MCP servers with proper metadata and permissions +- Created groups to organize related servers +- Tested your registry by listing and running both individual servers and groups + +You can now maintain your own curated list of MCP servers and groups tailored to +your organization's needs, organized into logical groups for different teams or +workflows. The registry can include both public servers from container +registries and private servers hosted within your organization. + +## Next steps + +- Explore the full [schema reference](../reference/registry-schema-toolhive.mdx) + to understand all available configuration options +- Learn about [custom permissions](../guides-cli/custom-permissions.mdx) for + fine-grained security control +- Set up [secrets management](../guides-cli/secrets-management.mdx) for servers + requiring API keys + +## Cleanup: Revert to the default registry + +If you want to switch back to using ToolHive's built-in registry after +completing this tutorial, you can easily revert your configuration. + + + + +To revert to the default registry through the UI: + +1. Navigate to **Settings** → **Registry** +2. Select the **Default Registry** option +3. Click **Save** to apply the configuration + +The UI will confirm that you're now using the built-in ToolHive registry. + + + + +To revert to the default registry using the CLI: + +```bash +thv config unset-registry +``` + +Verify that you're back to using the default registry: + +```bash +thv config get-registry +``` + +You should see a message indicating that the built-in registry is being used. + + + + +After reverting, all registry commands +([`thv registry list`](../reference/cli/thv_registry_list.md), +[`thv registry info`](../reference/cli/thv_registry_info.md), +[`thv search`](../reference/cli/thv_search.md)) will use ToolHive's built-in +registry instead of your custom one. diff --git a/versioned_docs/version-1.1/toolhive/tutorials/mcp-optimizer.mdx b/versioned_docs/version-1.1/toolhive/tutorials/mcp-optimizer.mdx new file mode 100644 index 00000000..b786b03a --- /dev/null +++ b/versioned_docs/version-1.1/toolhive/tutorials/mcp-optimizer.mdx @@ -0,0 +1,332 @@ +--- +title: Reduce token usage with MCP Optimizer +description: Enable the MCP Optimizer to enhance tool selection and reduce token usage. +--- + +## Overview + +The ToolHive MCP Optimizer acts as an intelligent intermediary between AI +clients and MCP servers. It provides tool discovery, unified access to multiple +MCP servers through a single endpoint, and intelligent routing of requests to +appropriate MCP tools. + +:::info[Status] + +The MCP Optimizer is currently experimental. If you try it out, please share +your feedback on the [Stacklok Discord community](https://discord.gg/stacklok). + +::: + +## About MCP Optimizer + +### Benefits + +- **Reduced token usage**: Narrow down the toolset to only relevant tools for a + given task, minimizing context overload and token consumption +- **Improved tool selection**: Find the most appropriate tools across all + connected MCP servers +- **Simplified client configuration**: Connect to a single MCP Optimizer + endpoint instead of managing multiple MCP server connections + +### How it works + +Instead of flooding the model with all available tools, MCP Optimizer introduces +two lightweight primitives: + +1. `find_tool`: Searches for the most relevant tools using hybrid semantic + + keyword search +2. `call_tool`: Routes the selected tool request to the appropriate MCP server + +The workflow is as follows: + +1. You send a prompt that requires tool assistance (for example, interacting + with a GitHub repo) +2. The assistant calls `find_tool` with relevant keywords extracted from the + prompt +3. MCP Optimizer returns the most relevant tools (up to 8 by default, but this + is configurable) +4. Only those tools and their descriptions are included in the context sent to + the model +5. The assistant uses `call_tool` to execute the task with the selected tool + +```mermaid +flowchart TB + subgraph optimizerGroup["MCP Optimizer group (internal)"] + direction TB + optimizer["MCP Optimizer"] + end + subgraph target["ToolHive group: default"] + direction TB + mcp1["MCP server"] + mcp2["MCP server"] + mcp3["MCP server"] + end + + client(["Client"]) <-- connects --> optimizerGroup + optimizer <-. discovers/routes .-> target +``` + +## Prerequisites + +- One of the following container runtimes: + - macOS: Docker Desktop, Podman Desktop, or Rancher Desktop (using dockerd) + - Windows: Docker Desktop or Rancher Desktop (using dockerd) + - Linux: Not currently supported +- ToolHive UI version 0.12.0 or later + +## Step 1: Install MCP servers in a ToolHive group + +Before you can use MCP Optimizer, you need to have one or more MCP servers +running in a ToolHive group. If you don't have any MCP servers set up yet, +follow these steps: + + + + +1. Open the ToolHive UI and go to the **MCP Servers** screen +2. Ensure you're in the `default` group (or create a new group if desired) +3. Run one or more MCP servers by clicking the **Add an MCP Server** button and + selecting from the registry or using a custom server image + + For this tutorial, you can run the following example MCP servers: + - `github`: Provides tools for interacting with GitHub repositories + ([guide](../guides-mcp/github.mdx?mode=ui)) + - `fetch`: Provides a web search tool to fetch recent news articles + - `time`: Provides a tool to get the current time in various time zones + +4. Wait for the MCP servers to start and become healthy + +See the [Run MCP servers](../guides-ui/run-mcp-servers.mdx) guide for more +details. + + + + +Run one or more MCP servers in the `default` group using the ToolHive CLI. For +this tutorial, you can run the following example MCP servers: + +- `github`: Provides tools for interacting with GitHub repositories + ([guide](../guides-mcp/github.mdx?mode=cli)) +- `fetch`: Provides a web search tool to fetch recent news articles +- `time`: Provides a tool to get the current time in various time zones + +```bash +thv run github +thv run fetch +thv run time +``` + +See the [Run MCP servers](../guides-cli/run-mcp-servers.mdx) guide for more +details. + +Verify the MCP servers are running: + +```bash +thv list +``` + + + + +## Step 2: Connect your AI client + +Connect your AI client to the ToolHive group where the MCP servers are running +(for example, the `default` group). + +:::note + +For best results, connect your client to only the optimized group. If you +connect it to multiple groups, ensure there is no overlap in MCP servers between +the groups to avoid unpredictable behavior. + +::: + + + + +Open the ToolHive UI and use the **Manage Clients** button on the main MCP +Servers screen to register your AI client with the appropriate group (for +example, `default`). + +See the [Client configuration](../guides-ui/client-configuration.mdx) guide for +more details. + + + + +Run the following command to register your AI client with the ToolHive group +where the MCP servers are running (for example, `default`): + +```bash +thv client setup +``` + +See the [Client configuration](../guides-cli/client-configuration.mdx) guide for +more details. + + + + +Open your AI client and verify that it is connected to the correct MCP servers. +If you installed the `github`, `fetch`, and `time` servers, you should see +almost 50 tools available. + +## Step 3: Enable MCP Optimizer + + + + +The ToolHive UI automates the setup of the MCP Optimizer. + +1. Open the **Settings** (⚙️) screen and enable **MCP Optimizer** under + **Experimental Features** +2. Click the **Configure** button on the notification that pops up, or go to the + main **MCP Servers** screen and click **MCP Optimizer** in the left sidebar +3. Select the group that contains the MCP servers you want to optimize and click + **Apply Changes** + +ToolHive automatically updates clients that were registered with the selected +group to use the MCP Optimizer. If you want to connect a new client, go to the +`default` group and use the **Manage Clients** button to register it. + +Open your AI client and check its MCP configuration. You should see a single MCP +server named `toolhive-mcp-optimizer`. + +:::info[What's happening?] + +When you enable MCP Optimizer, ToolHive automatically creates an internal group +and runs the `mcp-optimizer` MCP server in that group. + +The MCP Optimizer server discovers the MCP servers in the selected group and +builds an index of their tools for intelligent routing. Automatic polling keeps +the index up to date as servers are added or removed from the optimized group. + +ToolHive also disconnects your AI clients from the original MCP server group and +reconnects them to the MCP Optimizer group. + +::: + + + + +The ToolHive UI is the recommended way to set up MCP Optimizer, but you can also +do it manually with the ToolHive CLI. + +**Step 3.1: Run the API server** + +MCP Optimizer uses the ToolHive API server to discover MCP servers and manage +client connections. + +You can run the API server in two ways. The simplest is to install and run the +ToolHive UI, which automatically starts the API server in the background. + +If you prefer to run the API server manually using the CLI, open a dedicated +terminal window and start it on a specific port: + +```bash +thv serve --port 50100 +``` + +Note the port number (`50100` in this example) for use in the next step. + +**Step 3.2: Create a dedicated group and run mcp-optimizer** + +```bash +# Create the meta group +thv group create optimizer + +# Run mcp-optimizer in the dedicated group +thv run --group optimizer -e TOOLHIVE_PORT=50100 mcp-optimizer +``` + +If you are running the API server using the ToolHive UI, omit the +`TOOLHIVE_PORT` environment variable. + +**Step 3.3: Configure your AI client for the meta group** + +Remove your client from the `default` group. For example, to unregister Cursor: + +```bash +thv client remove cursor --group default +``` + +Then, register your client with the `optimizer` group: + +```bash +# Run the group setup, select the optimizer group, and then select your client +thv client setup + +# Verify the configuration +thv client list-registered +``` + +:::note + +Your client now connects only to the `optimizer` group and sees only the +`mcp-optimizer` MCP server. + +::: + +The resulting configuration should look like this: + +```mermaid +flowchart TB + subgraph meta["ToolHive group: optimizer"] + direction TB + optimizer["mcp-optimizer"] + end + subgraph def["ToolHive group: default"] + direction TB + mcp1["github"] + mcp2["fetch"] + mcp3["time"] + end + + client(["Client"]) <-- connects --> meta + optimizer <-. discovers/routes .-> def + client x-. 🚫 .-x def +``` + + + + +## Step 4: Sample prompts + +After you configure and run MCP Optimizer, you can use the same prompts you +would normally use with individual MCP servers. The Optimizer automatically +discovers and routes to appropriate tools. + +Using the example MCP servers above, here are some sample prompts: + +- "Get the details of GitHub issue 1911 from the stacklok/toolhive repo" +- "List recent PRs from the stacklok/toolhive repo" +- "Fetch the latest news articles about AI" +- "What is the current time in Tokyo?" + +Watch how MCP Optimizer intelligently selects and routes to the relevant tools +across the connected MCP servers, reducing token usage and improving response +quality. + +To check your token savings, you can ask the optimizer: + +- "How many tokens did I save using MCP Optimizer?" + +## What's next? + +Now that you've set up MCP Optimizer, consider exploring these next steps: + +- Experiment with different MCP servers to see how MCP Optimizer enhances tool + selection and reduces token usage +- Monitor the performance and effectiveness of MCP Optimizer in your AI + applications +- Use the [ToolHive Playground](../guides-ui/playground.mdx) to test and refine + your prompts with MCP Optimizer +- Provide feedback on your experience with MCP Optimizer on the + [Stacklok Discord community](https://discord.gg/stacklok) + +## Related information + +- [MCP Optimizer UI guide](../guides-ui/mcp-optimizer.mdx) +- [Optimize tool discovery in vMCP](../guides-vmcp/optimizer.mdx) - Kubernetes + operator approach +- [Organize MCP servers into groups](../guides-ui/group-management.mdx) diff --git a/versioned_sidebars/version-1.0-sidebars.json b/versioned_sidebars/version-1.0-sidebars.json new file mode 100644 index 00000000..7549e616 --- /dev/null +++ b/versioned_sidebars/version-1.0-sidebars.json @@ -0,0 +1,839 @@ +{ + "toolhiveSidebar": [ + "toolhive/index", + { + "type": "category", + "label": "ToolHive UI", + "description": "How to use the ToolHive desktop application", + "link": { + "type": "doc", + "id": "toolhive/guides-ui/index" + }, + "items": [ + "toolhive/guides-ui/quickstart", + "toolhive/guides-ui/install", + "toolhive/guides-ui/registry", + { + "type": "category", + "label": "Run MCP servers", + "description": "How to install and run MCP servers in the ToolHive UI", + "collapsed": false, + "collapsible": false, + "link": { + "type": "doc", + "id": "toolhive/guides-ui/run-mcp-servers" + }, + "items": [ + "toolhive/guides-ui/group-management", + "toolhive/guides-ui/secrets-management", + "toolhive/guides-ui/network-isolation", + "toolhive/guides-ui/customize-tools" + ] + }, + "toolhive/guides-ui/client-configuration", + "toolhive/guides-ui/cli-access", + "toolhive/guides-ui/mcp-optimizer", + "toolhive/guides-ui/playground" + ] + }, + { + "type": "category", + "label": "ToolHive CLI", + "description": "How to use the ToolHive CLI for managing MCP servers", + "link": { + "type": "doc", + "id": "toolhive/guides-cli/index" + }, + "items": [ + "toolhive/guides-cli/quickstart", + "toolhive/guides-cli/install", + "toolhive/guides-cli/registry", + { + "type": "category", + "label": "Run MCP servers", + "description": "How to run MCP servers with the ToolHive CLI", + "collapsed": false, + "collapsible": false, + "link": { + "type": "doc", + "id": "toolhive/guides-cli/run-mcp-servers" + }, + "items": [ + "toolhive/guides-cli/manage-mcp-servers", + "toolhive/guides-cli/group-management", + "toolhive/guides-cli/secrets-management" + ] + }, + "toolhive/guides-cli/client-configuration", + { + "type": "category", + "label": "Permissions and security", + "description": "How to configure filesystem and network access for MCP servers", + "collapsed": false, + "collapsible": false, + "link": { + "type": "doc", + "id": "toolhive/guides-cli/custom-permissions" + }, + "items": [ + "toolhive/guides-cli/filesystem-access", + "toolhive/guides-cli/thvignore", + "toolhive/guides-cli/network-isolation" + ] + }, + { + "type": "category", + "label": "Advanced workflows", + "description": "Build, test, secure, and automate", + "collapsed": true, + "items": [ + "toolhive/guides-cli/auth", + "toolhive/guides-cli/token-exchange", + "toolhive/guides-cli/telemetry-and-metrics", + "toolhive/guides-cli/test-mcp-servers", + "toolhive/guides-cli/build-containers", + "toolhive/guides-cli/advanced-cicd", + { + "type": "category", + "label": "API server", + "description": "How to set up and use the ToolHive API server", + "link": { + "type": "doc", + "id": "toolhive/guides-cli/api-server" + }, + "items": ["toolhive/reference/api"] + } + ] + }, + { + "type": "category", + "label": "Command reference", + "description": "Detailed reference for ToolHive CLI commands", + "collapsed": true, + "items": [ + { + "type": "category", + "label": "thv", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_build", + "label": "build" + }, + { + "type": "category", + "label": "client", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_client_list-registered", + "label": "list-registered" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_client_register", + "label": "register" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_client_remove", + "label": "remove" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_client_setup", + "label": "setup" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_client_status", + "label": "status" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_client" + } + }, + { + "type": "category", + "label": "config", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_get-build-auth-file", + "label": "get-build-auth-file" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_get-build-env", + "label": "get-build-env" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_get-ca-cert", + "label": "get-ca-cert" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_get-registry", + "label": "get-registry" + }, + { + "type": "category", + "label": "otel", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_get-enable-prometheus-metrics-path", + "label": "get-enable-prometheus-metrics-path" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_get-endpoint", + "label": "get-endpoint" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_get-env-vars", + "label": "get-env-vars" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_get-insecure", + "label": "get-insecure" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_get-metrics-enabled", + "label": "get-metrics-enabled" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_get-sampling-rate", + "label": "get-sampling-rate" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_get-tracing-enabled", + "label": "get-tracing-enabled" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_set-enable-prometheus-metrics-path", + "label": "set-enable-prometheus-metrics-path" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_set-endpoint", + "label": "set-endpoint" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_set-env-vars", + "label": "set-env-vars" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_set-insecure", + "label": "set-insecure" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_set-metrics-enabled", + "label": "set-metrics-enabled" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_set-sampling-rate", + "label": "set-sampling-rate" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_set-tracing-enabled", + "label": "set-tracing-enabled" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_unset-enable-prometheus-metrics-path", + "label": "unset-enable-prometheus-metrics-path" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_unset-endpoint", + "label": "unset-endpoint" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_unset-env-vars", + "label": "unset-env-vars" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_unset-insecure", + "label": "unset-insecure" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_unset-metrics-enabled", + "label": "unset-metrics-enabled" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_unset-sampling-rate", + "label": "unset-sampling-rate" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_unset-tracing-enabled", + "label": "unset-tracing-enabled" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel" + } + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_set-build-auth-file", + "label": "set-build-auth-file" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_set-build-env", + "label": "set-build-env" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_set-ca-cert", + "label": "set-ca-cert" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_set-registry-auth", + "label": "set-registry-auth" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_set-registry", + "label": "set-registry" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_unset-build-auth-file", + "label": "unset-build-auth-file" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_unset-build-env", + "label": "unset-build-env" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_unset-ca-cert", + "label": "unset-ca-cert" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_unset-registry-auth", + "label": "unset-registry-auth" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_unset-registry", + "label": "unset-registry" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_usage-metrics", + "label": "usage-metrics" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_config" + } + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_export", + "label": "export" + }, + { + "type": "category", + "label": "group", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_group_create", + "label": "create" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_group_list", + "label": "list" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_group_rm", + "label": "rm" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_group_run", + "label": "run" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_group" + } + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_inspector", + "label": "inspector" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_list", + "label": "list" + }, + { + "type": "category", + "label": "logs", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_logs_prune", + "label": "prune" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_logs" + } + }, + { + "type": "category", + "label": "mcp", + "collapsed": true, + "items": [ + { + "type": "category", + "label": "list", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_mcp_list_prompts", + "label": "prompts" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_mcp_list_resources", + "label": "resources" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_mcp_list_tools", + "label": "tools" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_mcp_list" + } + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_mcp_serve", + "label": "serve" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_mcp" + } + }, + { + "type": "category", + "label": "proxy", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_proxy_stdio", + "label": "stdio" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_proxy_tunnel", + "label": "tunnel" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_proxy" + } + }, + { + "type": "category", + "label": "registry", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_registry_info", + "label": "info" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_registry_list", + "label": "list" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_registry_login", + "label": "login" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_registry_logout", + "label": "logout" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_registry" + } + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_rm", + "label": "rm" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_run", + "label": "run" + }, + { + "type": "category", + "label": "runtime", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_runtime_check", + "label": "check" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_runtime" + } + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_search", + "label": "search" + }, + { + "type": "category", + "label": "secret", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_secret_delete", + "label": "delete" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_secret_get", + "label": "get" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_secret_list", + "label": "list" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_secret_provider", + "label": "provider" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_secret_reset-keyring", + "label": "reset-keyring" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_secret_set", + "label": "set" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_secret_setup", + "label": "setup" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_secret" + } + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_serve", + "label": "serve" + }, + { + "type": "category", + "label": "skill", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_skill_build", + "label": "build" + }, + { + "type": "category", + "label": "builds", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_skill_builds_remove", + "label": "remove" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_skill_builds" + } + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_skill_info", + "label": "info" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_skill_install", + "label": "install" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_skill_list", + "label": "list" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_skill_push", + "label": "push" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_skill_uninstall", + "label": "uninstall" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_skill_validate", + "label": "validate" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_skill" + } + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_start", + "label": "start" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_status", + "label": "status" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_stop", + "label": "stop" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_version", + "label": "version" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv" + } + } + ] + } + ] + }, + { + "type": "category", + "label": "Kubernetes Operator", + "description": "How to deploy and manage ToolHive on Kubernetes", + "link": { + "type": "doc", + "id": "toolhive/guides-k8s/index" + }, + "items": [ + "toolhive/guides-k8s/intro", + "toolhive/guides-k8s/quickstart", + "toolhive/guides-k8s/deploy-operator", + "toolhive/guides-k8s/run-mcp-k8s", + "toolhive/guides-k8s/remote-mcp-proxy", + "toolhive/guides-k8s/connect-clients", + "toolhive/guides-k8s/customize-tools", + "toolhive/guides-k8s/auth-k8s", + "toolhive/guides-k8s/redis-session-storage", + "toolhive/guides-k8s/token-exchange-k8s", + "toolhive/guides-k8s/telemetry-and-metrics", + "toolhive/guides-k8s/logging", + "toolhive/reference/crd-spec" + ] + }, + { + "type": "category", + "label": "Virtual MCP Server", + "description": "How to aggregate multiple MCP servers into a unified endpoint", + "link": { + "type": "doc", + "id": "toolhive/guides-vmcp/index" + }, + "items": [ + "toolhive/guides-vmcp/intro", + "toolhive/guides-vmcp/quickstart", + "toolhive/guides-vmcp/configuration", + "toolhive/guides-vmcp/backend-discovery", + "toolhive/guides-vmcp/authentication", + "toolhive/guides-vmcp/tool-aggregation", + "toolhive/guides-vmcp/composite-tools", + "toolhive/guides-vmcp/optimizer", + "toolhive/guides-vmcp/failure-handling", + "toolhive/guides-vmcp/telemetry-and-metrics", + "toolhive/guides-vmcp/audit-logging", + "toolhive/guides-vmcp/scaling-and-performance" + ] + }, + { + "type": "category", + "label": "Registry Server", + "description": "How to deploy and use the ToolHive Registry server to discover and access MCP servers and skills", + "link": { + "type": "doc", + "id": "toolhive/guides-registry/index" + }, + "items": [ + "toolhive/guides-registry/intro", + "toolhive/guides-registry/deployment", + "toolhive/guides-registry/deploy-operator", + "toolhive/guides-registry/deploy-manual", + "toolhive/guides-registry/configuration", + "toolhive/guides-registry/authentication", + "toolhive/guides-registry/database", + "toolhive/guides-registry/skills", + "toolhive/guides-registry/telemetry-metrics", + "toolhive/reference/registry-api", + "toolhive/reference/registry-schema-upstream", + "toolhive/reference/registry-schema-toolhive" + ] + }, + { + "type": "category", + "label": "Concepts", + "description": "Core concepts and architecture of ToolHive and MCP", + "link": { + "type": "generated-index", + "slug": "toolhive/concepts", + "description": "Learn about the key concepts behind ToolHive and the Model Context Protocol (MCP)." + }, + "items": [ + "toolhive/concepts/mcp-primer", + "toolhive/concepts/groups", + "toolhive/concepts/tool-optimization", + "toolhive/concepts/registry-criteria", + "toolhive/concepts/observability", + "toolhive/concepts/auth-framework", + "toolhive/concepts/cedar-policies", + "toolhive/concepts/backend-auth", + "toolhive/concepts/vmcp", + "toolhive/concepts/skills" + ] + }, + { + "type": "category", + "label": "Integrations", + "description": "Connect ToolHive with third-party tools and services", + "link": { + "type": "generated-index", + "slug": "toolhive/integrations", + "description": "Guides for integrating ToolHive with third-party tools and services like OpenTelemetry, HashiCorp Vault, and ngrok." + }, + "items": [ + "toolhive/integrations/opentelemetry", + "toolhive/integrations/vault", + "toolhive/integrations/aws-sts", + "toolhive/integrations/ingress-ngrok", + "toolhive/integrations/okta" + ] + }, + { + "type": "category", + "label": "Tutorials", + "description": "End-to-end tutorials covering multiple ToolHive components", + "link": { + "type": "generated-index", + "slug": "toolhive/tutorials", + "description": "End-to-end tutorials that span multiple ToolHive components." + }, + "items": [ + "toolhive/tutorials/custom-registry", + "toolhive/tutorials/mcp-optimizer" + ] + }, + { + "type": "category", + "label": "MCP server guides", + "description": "How to configure and use MCP servers for different use cases", + "link": { + "type": "generated-index", + "slug": "toolhive/guides-mcp", + "title": "MCP server usage guides", + "description": "These guides provide step-by-step instructions for using various MCP servers with ToolHive. They cover everything from installation to advanced configuration options." + }, + "items": [ + { + "type": "autogenerated", + "dirName": "toolhive/guides-mcp" + } + ] + }, + "toolhive/reference/client-compatibility", + "toolhive/reference/index", + "toolhive/faq", + "toolhive/enterprise", + "toolhive/support", + "toolhive/contributing" + ] +} diff --git a/versioned_sidebars/version-1.1-sidebars.json b/versioned_sidebars/version-1.1-sidebars.json new file mode 100644 index 00000000..5569dd85 --- /dev/null +++ b/versioned_sidebars/version-1.1-sidebars.json @@ -0,0 +1,830 @@ +{ + "toolhiveSidebar": [ + "toolhive/index", + { + "type": "category", + "label": "ToolHive UI", + "description": "How to use the ToolHive desktop application", + "link": { + "type": "doc", + "id": "toolhive/guides-ui/index" + }, + "items": [ + "toolhive/guides-ui/quickstart", + "toolhive/guides-ui/install", + "toolhive/guides-ui/registry", + { + "type": "category", + "label": "Run MCP servers", + "description": "How to install and run MCP servers in the ToolHive UI", + "collapsed": false, + "collapsible": false, + "link": { + "type": "doc", + "id": "toolhive/guides-ui/run-mcp-servers" + }, + "items": [ + "toolhive/guides-ui/group-management", + "toolhive/guides-ui/secrets-management", + "toolhive/guides-ui/network-isolation", + "toolhive/guides-ui/customize-tools" + ] + }, + "toolhive/guides-ui/client-configuration", + "toolhive/guides-ui/cli-access", + "toolhive/guides-ui/mcp-optimizer", + "toolhive/guides-ui/playground" + ] + }, + { + "type": "category", + "label": "ToolHive CLI", + "description": "How to use the ToolHive CLI for managing MCP servers", + "link": { + "type": "doc", + "id": "toolhive/guides-cli/index" + }, + "items": [ + "toolhive/guides-cli/quickstart", + "toolhive/guides-cli/install", + "toolhive/guides-cli/registry", + { + "type": "category", + "label": "Run MCP servers", + "description": "How to run MCP servers with the ToolHive CLI", + "collapsed": false, + "collapsible": false, + "link": { + "type": "doc", + "id": "toolhive/guides-cli/run-mcp-servers" + }, + "items": [ + "toolhive/guides-cli/manage-mcp-servers", + "toolhive/guides-cli/group-management", + "toolhive/guides-cli/secrets-management" + ] + }, + "toolhive/guides-cli/client-configuration", + "toolhive/guides-cli/skills-management", + { + "type": "category", + "label": "Permissions and security", + "description": "How to configure filesystem and network access for MCP servers", + "collapsed": false, + "collapsible": false, + "link": { + "type": "doc", + "id": "toolhive/guides-cli/custom-permissions" + }, + "items": [ + "toolhive/guides-cli/filesystem-access", + "toolhive/guides-cli/thvignore", + "toolhive/guides-cli/network-isolation" + ] + }, + { + "type": "category", + "label": "Advanced workflows", + "description": "Build, test, secure, and automate", + "collapsed": true, + "items": [ + "toolhive/guides-cli/auth", + "toolhive/guides-cli/token-exchange", + "toolhive/guides-cli/telemetry-and-metrics", + "toolhive/guides-cli/test-mcp-servers", + "toolhive/guides-cli/build-containers", + "toolhive/guides-cli/advanced-cicd", + { + "type": "category", + "label": "API server", + "description": "How to set up and use the ToolHive API server", + "link": { + "type": "doc", + "id": "toolhive/guides-cli/api-server" + }, + "items": ["toolhive/reference/api"] + } + ] + }, + { + "type": "category", + "label": "Command reference", + "description": "Detailed reference for ToolHive CLI commands", + "collapsed": true, + "items": [ + { + "type": "category", + "label": "thv", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_build", + "label": "build" + }, + { + "type": "category", + "label": "client", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_client_list-registered", + "label": "list-registered" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_client_register", + "label": "register" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_client_remove", + "label": "remove" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_client_setup", + "label": "setup" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_client_status", + "label": "status" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_client" + } + }, + { + "type": "category", + "label": "config", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_get-build-auth-file", + "label": "get-build-auth-file" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_get-build-env", + "label": "get-build-env" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_get-ca-cert", + "label": "get-ca-cert" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_get-registry", + "label": "get-registry" + }, + { + "type": "category", + "label": "otel", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_get-enable-prometheus-metrics-path", + "label": "get-enable-prometheus-metrics-path" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_get-endpoint", + "label": "get-endpoint" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_get-env-vars", + "label": "get-env-vars" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_get-insecure", + "label": "get-insecure" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_get-metrics-enabled", + "label": "get-metrics-enabled" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_get-sampling-rate", + "label": "get-sampling-rate" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_get-tracing-enabled", + "label": "get-tracing-enabled" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_set-enable-prometheus-metrics-path", + "label": "set-enable-prometheus-metrics-path" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_set-endpoint", + "label": "set-endpoint" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_set-env-vars", + "label": "set-env-vars" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_set-insecure", + "label": "set-insecure" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_set-metrics-enabled", + "label": "set-metrics-enabled" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_set-sampling-rate", + "label": "set-sampling-rate" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_set-tracing-enabled", + "label": "set-tracing-enabled" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_unset-enable-prometheus-metrics-path", + "label": "unset-enable-prometheus-metrics-path" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_unset-endpoint", + "label": "unset-endpoint" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_unset-env-vars", + "label": "unset-env-vars" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_unset-insecure", + "label": "unset-insecure" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_unset-metrics-enabled", + "label": "unset-metrics-enabled" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_unset-sampling-rate", + "label": "unset-sampling-rate" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel_unset-tracing-enabled", + "label": "unset-tracing-enabled" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_otel" + } + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_set-build-auth-file", + "label": "set-build-auth-file" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_set-build-env", + "label": "set-build-env" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_set-ca-cert", + "label": "set-ca-cert" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_set-registry", + "label": "set-registry" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_unset-build-auth-file", + "label": "unset-build-auth-file" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_unset-build-env", + "label": "unset-build-env" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_unset-ca-cert", + "label": "unset-ca-cert" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_unset-registry", + "label": "unset-registry" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_config_usage-metrics", + "label": "usage-metrics" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_config" + } + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_export", + "label": "export" + }, + { + "type": "category", + "label": "group", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_group_create", + "label": "create" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_group_list", + "label": "list" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_group_rm", + "label": "rm" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_group_run", + "label": "run" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_group" + } + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_inspector", + "label": "inspector" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_list", + "label": "list" + }, + { + "type": "category", + "label": "logs", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_logs_prune", + "label": "prune" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_logs" + } + }, + { + "type": "category", + "label": "mcp", + "collapsed": true, + "items": [ + { + "type": "category", + "label": "list", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_mcp_list_prompts", + "label": "prompts" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_mcp_list_resources", + "label": "resources" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_mcp_list_tools", + "label": "tools" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_mcp_list" + } + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_mcp_serve", + "label": "serve" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_mcp" + } + }, + { + "type": "category", + "label": "proxy", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_proxy_stdio", + "label": "stdio" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_proxy_tunnel", + "label": "tunnel" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_proxy" + } + }, + { + "type": "category", + "label": "registry", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_registry_info", + "label": "info" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_registry_list", + "label": "list" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_registry_login", + "label": "login" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_registry_logout", + "label": "logout" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_registry" + } + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_rm", + "label": "rm" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_run", + "label": "run" + }, + { + "type": "category", + "label": "runtime", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_runtime_check", + "label": "check" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_runtime" + } + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_search", + "label": "search" + }, + { + "type": "category", + "label": "secret", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_secret_delete", + "label": "delete" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_secret_get", + "label": "get" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_secret_list", + "label": "list" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_secret_provider", + "label": "provider" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_secret_reset-keyring", + "label": "reset-keyring" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_secret_set", + "label": "set" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_secret_setup", + "label": "setup" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_secret" + } + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_serve", + "label": "serve" + }, + { + "type": "category", + "label": "skill", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_skill_build", + "label": "build" + }, + { + "type": "category", + "label": "builds", + "collapsed": true, + "items": [ + { + "type": "doc", + "id": "toolhive/reference/cli/thv_skill_builds_remove", + "label": "remove" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_skill_builds" + } + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_skill_info", + "label": "info" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_skill_install", + "label": "install" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_skill_list", + "label": "list" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_skill_push", + "label": "push" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_skill_uninstall", + "label": "uninstall" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_skill_validate", + "label": "validate" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv_skill" + } + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_start", + "label": "start" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_status", + "label": "status" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_stop", + "label": "stop" + }, + { + "type": "doc", + "id": "toolhive/reference/cli/thv_version", + "label": "version" + } + ], + "link": { + "type": "doc", + "id": "toolhive/reference/cli/thv" + } + } + ] + } + ] + }, + { + "type": "category", + "label": "Kubernetes Operator", + "description": "How to deploy and manage ToolHive on Kubernetes", + "link": { + "type": "doc", + "id": "toolhive/guides-k8s/index" + }, + "items": [ + "toolhive/guides-k8s/intro", + "toolhive/guides-k8s/quickstart", + "toolhive/guides-k8s/deploy-operator", + "toolhive/guides-k8s/run-mcp-k8s", + "toolhive/guides-k8s/remote-mcp-proxy", + "toolhive/guides-k8s/connect-clients", + "toolhive/guides-k8s/customize-tools", + "toolhive/guides-k8s/auth-k8s", + "toolhive/guides-k8s/redis-session-storage", + "toolhive/guides-k8s/token-exchange-k8s", + "toolhive/guides-k8s/telemetry-and-metrics", + "toolhive/guides-k8s/logging", + "toolhive/reference/crd-spec" + ] + }, + { + "type": "category", + "label": "Virtual MCP Server", + "description": "How to aggregate multiple MCP servers into a unified endpoint", + "link": { + "type": "doc", + "id": "toolhive/guides-vmcp/index" + }, + "items": [ + "toolhive/guides-vmcp/intro", + "toolhive/guides-vmcp/quickstart", + "toolhive/guides-vmcp/configuration", + "toolhive/guides-vmcp/backend-discovery", + "toolhive/guides-vmcp/authentication", + "toolhive/guides-vmcp/tool-aggregation", + "toolhive/guides-vmcp/composite-tools", + "toolhive/guides-vmcp/optimizer", + "toolhive/guides-vmcp/failure-handling", + "toolhive/guides-vmcp/telemetry-and-metrics", + "toolhive/guides-vmcp/audit-logging", + "toolhive/guides-vmcp/scaling-and-performance" + ] + }, + { + "type": "category", + "label": "Registry Server", + "description": "How to deploy and use the ToolHive Registry server to discover and access MCP servers and skills", + "link": { + "type": "doc", + "id": "toolhive/guides-registry/index" + }, + "items": [ + "toolhive/guides-registry/intro", + "toolhive/guides-registry/deployment", + "toolhive/guides-registry/deploy-operator", + "toolhive/guides-registry/deploy-manual", + "toolhive/guides-registry/configuration", + "toolhive/guides-registry/authentication", + "toolhive/guides-registry/database", + "toolhive/guides-registry/skills", + "toolhive/guides-registry/telemetry-metrics", + "toolhive/reference/registry-api", + "toolhive/reference/registry-schema-upstream", + "toolhive/reference/registry-schema-toolhive" + ] + }, + { + "type": "category", + "label": "Concepts", + "description": "Core concepts and architecture of ToolHive and MCP", + "link": { + "type": "generated-index", + "slug": "toolhive/concepts", + "description": "Learn about the key concepts behind ToolHive and the Model Context Protocol (MCP)." + }, + "items": [ + "toolhive/concepts/mcp-primer", + "toolhive/concepts/groups", + "toolhive/concepts/tool-optimization", + "toolhive/concepts/registry-criteria", + "toolhive/concepts/observability", + "toolhive/concepts/auth-framework", + "toolhive/concepts/cedar-policies", + "toolhive/concepts/backend-auth", + "toolhive/concepts/vmcp", + "toolhive/concepts/skills" + ] + }, + { + "type": "category", + "label": "Integrations", + "description": "Connect ToolHive with third-party tools and services", + "link": { + "type": "generated-index", + "slug": "toolhive/integrations", + "description": "Guides for integrating ToolHive with third-party tools and services like OpenTelemetry, HashiCorp Vault, and ngrok." + }, + "items": [ + "toolhive/integrations/opentelemetry", + "toolhive/integrations/vault", + "toolhive/integrations/aws-sts", + "toolhive/integrations/ingress-ngrok", + "toolhive/integrations/okta" + ] + }, + { + "type": "category", + "label": "Tutorials", + "description": "End-to-end tutorials covering multiple ToolHive components", + "link": { + "type": "generated-index", + "slug": "toolhive/tutorials", + "description": "End-to-end tutorials that span multiple ToolHive components." + }, + "items": [ + "toolhive/tutorials/custom-registry", + "toolhive/tutorials/mcp-optimizer" + ] + }, + { + "type": "category", + "label": "MCP server guides", + "description": "How to configure and use MCP servers for different use cases", + "link": { + "type": "generated-index", + "slug": "toolhive/guides-mcp", + "title": "MCP server usage guides", + "description": "These guides provide step-by-step instructions for using various MCP servers with ToolHive. They cover everything from installation to advanced configuration options." + }, + "items": [ + { + "type": "autogenerated", + "dirName": "toolhive/guides-mcp" + } + ] + }, + "toolhive/reference/client-compatibility", + "toolhive/reference/index", + "toolhive/faq", + "toolhive/enterprise", + "toolhive/support", + "toolhive/contributing" + ] +} diff --git a/versions.json b/versions.json new file mode 100644 index 00000000..d07ea4d6 --- /dev/null +++ b/versions.json @@ -0,0 +1 @@ +["1.1", "1.0"]