Capability bundles from a URL
A bundle is a packaged MCP server or Agent Skill that you host yourself. Workspace owners and admins add it by URL, and members' computers download, verify and run it locally when an agent, workflow step or assistant uses it. Publishers keep their source private and release new versions on their own schedule.
Bundles extend the Capability Catalog. Assignment, access policies and required or optional rules work the same way as for any other capability.
Add a bundle
- Open Settings → Capabilities, then Add MCP or Add skill.
- Select the Bundle URL tab.
- Enter an HTTPS URL for a package or a release feed. The URL can't contain a username, password, query string or fragment.
- Choose Authentication: None, Bearer token (recommended) or Basic auth, and enter the credential.
- Select Validate. One Horizon downloads the bundle, checks it without running it, and shows its name, version, server type, signer, platforms and file count.
- Read the Third-party code notice, check I trust this publisher, then select Add.
An MCP server with a secret setting continues to its authentication step, where you add the token the server needs at run time. That runtime token is separate from the download credential.
Members need One Horizon Desktop or the CLI at a version that supports bundles. Older versions see "Update One Horizon Desktop or CLI to use this bundle." A required bundle stops the run before it starts; an optional one is listed as a limitation.
Package formats
| Type | Format | Rules |
|---|---|---|
| MCP server | MCPB package (.mcpb) | manifest.json at the root with manifest_version 0.2, 0.3 or 0.4. Server type node, python, binary, or uv (needs 0.4). Legacy DXT packages must be repacked with mcpb pack. |
| Agent Skill | ZIP | Exactly one folder with a SKILL.md. name is 1 to 64 lowercase letters, digits and single hyphens, and matches its folder. description is 1 to 1024 characters. compatibility, when set, is at most 500 characters. |
A bundle holds one MCP server or one skill, with its supporting files. A ZIP with several skills is rejected with the folders it found; publish each skill separately.
MCPB user_config. Fields with a default are filled in from that default. At most one sensitive string field is supported, and it must fill one whole environment variable in server.mcp_config.env; admins set its value in the server's authentication settings. directory and file fields, multiple fields, required fields without a default, and secrets in arguments are rejected with the field names.
Versions. A feed version is used as is. For a direct URL, the MCPB version or the skill's metadata.version is used. Without one, the version is sha256- plus the first 12 characters of the package digest.
Publish a release feed
A release feed lists versions and per-platform packages. One Horizon installs the highest stable version and ignores prereleases.
{ "schemaVersion": 1, "type": "mcp", "name": "acme-search", "versions": [ { "version": "1.5.0", "packages": [ { "url": "packages/acme-search-1.5.0-darwin-arm64.mcpb", "sha256": "4f2a…", "platform": "darwin", "arch": "arm64" }, { "url": "packages/acme-search-1.5.0-linux-x64.mcpb", "sha256": "9c1e…", "size": 18432000, "platform": "linux", "arch": "x64" }, { "url": "https://downloads.acme.com/acme-search-1.5.0-win32-x64.mcpb", "sha256": "b70d…", "platform": "win32", "arch": "x64" } ] } ]}
| Field | Rule |
|---|---|
schemaVersion | Required. Must be 1. |
type | Required. mcp or skill, matching Add MCP or Add skill. |
name | Required. Must equal the package's MCPB or skill name. |
versions[] | Required. The first 50 entries are read. |
versions[].version | Semantic version, such as 1.5.0. Prereleases like 2.0.0-beta.1 are ignored. |
versions[].packages[] | 1 to 8 packages. url (HTTPS, absolute or relative to the feed) and sha256 (64 hex characters) are required. size, platform (darwin, linux, win32) and arch (x64, arm64) are optional. |
A package without platform and arch runs anywhere its manifest allows. For a binary server, One Horizon reads the executable's header to confirm its operating system and CPU; a universal macOS binary covers both x64 and arm64.
Hosting and authentication
- The download credential is bound to the origin of the URL you entered, such as
https://packages.acme.com. One Horizon sends it only to that origin. - Package links and redirects to another origin, such as signed S3 or CDN URLs, are fetched without the credential. Once a redirect leaves the origin, the credential isn't sent again, even if a later redirect returns.
- Treat signed URLs as secrets. One Horizon uses them once and never stores, logs or shows them.
- Up to 3 redirects are followed, all over HTTPS. Addresses in private, loopback, link-local or other reserved ranges are refused.
- Credentials are stored encrypted and never shown again. Members' computers never receive them.
Integrity, signing and trust
- Every package is pinned by its SHA-256 digest. A feed must publish
sha256for each package, and a mismatch stops the install. A direct URL is pinned to the bytes One Horizon downloaded. - MCPB packages signed with
mcpb signare checked. A signature that doesn't match the package is rejected. A valid signature shows its signer; one that doesn't chain to a trusted certificate authority shows as "not verified". Unsigned packages show Unsigned. Skill ZIPs have no signature format and rely on the digest. - Validation checks structure and integrity. It doesn't check whether the software is safe. A bundle runs on each member's computer with that member's permissions, so add bundles only from publishers you trust.
- One Horizon never runs bundle code on its servers. Inspection only reads files.
Updates and pinning
Open the bundle in Settings → Capabilities:
- Check for updates downloads and validates the latest version. A new version, or the same version number with changed contents, is staged as available. The installed version keeps working.
- Update to a new version requires the same trust confirmation as adding the bundle.
- Discard update drops the staged version.
Sessions that already started keep the exact version and digest they began with. New sessions use the approved version.
Replace token rotates the download credential in place. Remove credential deletes it. Without a working credential, checks and updates for a private bundle fail with an authentication error, while versions you already approved keep working until you remove the bundle.
On members' computers
Before a run or an assistant turn, Desktop and the CLI pick the package for the computer's operating system and CPU, download it from One Horizon, and verify its digest. Verified packages are kept read-only in ~/.one/capabilities/cache/packages, so a later start uses the cached copy without downloading. A cached package that changed is removed and downloaded again once.
Node.js, Python and uv servers need that runtime on PATH, at the version the manifest requires. Run activity shows when a package is downloading and how long preparation took.
Limits
| Limit | Value |
|---|---|
| Release feed | 1 MiB |
| One package download | 70 MB |
| Files in a package | 10,000 |
| Expanded package size | 100 MB |
| Compression ratio per file | 100:1 for files over 64 KiB |
| Download time | 15 seconds to the first response, 120 seconds in total |
Archives are also rejected for .. paths, absolute, drive-letter or UNC paths, backslashes, Windows reserved names, names ending in a dot or space, links, and names that differ only by case.
Troubleshooting
| Error code | What to do |
|---|---|
bundle_url_invalid | Use an HTTPS URL without a username, password, query string or fragment. Put tokens in Authentication. |
bundle_unreachable | Check that the host resolves to a public address, has a valid certificate and responds. |
bundle_auth_required | The publisher needs a credential. Choose Bearer token or Basic auth. |
bundle_auth_rejected | The publisher rejected the credential, or it belongs to another origin. Replace the token. |
bundle_not_found | The URL returned 404. Check the path. |
bundle_metadata_invalid | A feed or manifest field is wrong. The message names the field. |
bundle_unsupported_version | Use feed schemaVersion 1 or MCPB manifest_version 0.2 to 0.4. |
bundle_archive_corrupt | The ZIP is truncated or unreadable. Rebuild and upload it again. |
bundle_layout_invalid | The package is missing manifest.json or SKILL.md, holds more than one capability, or was added with the wrong Add option. |
bundle_unsafe_entry | An archive entry breaks an extraction rule. The message names the rule. |
bundle_too_large | The feed, package or archive is over a limit. |
bundle_integrity_mismatch | A package doesn't match its published sha256 or size. Fix the feed. |
bundle_signature_invalid | The MCPB signature doesn't match. Ask the publisher for a new build. |
bundle_platform_missing | No package targets Windows, macOS or Linux. |
bundle_config_unsupported | Remove or give defaults to the listed user_config fields. |
bundle_platform_unsupported | This computer's OS and CPU have no package. Ask the publisher for that build. |
bundle_runtime_missing | Install the Node.js, Python or uv version the message names. |
bundle_download_failed | Check the connection and start again. |
bundle_disk_error, bundle_permission_error | Free disk space, or fix the permissions of ~/.one/capabilities. |
client_upgrade_required | Update One Horizon Desktop or the CLI. |
Test a bundle source
The repository's acceptance suite runs a local publisher simulator with these cases, including authentication, signed redirects and hostile archives:
yarn workspace @one/tools-acceptance test:bundlesGateway's inspection rules have their own tests:
yarn go:test ./apps/api/gateway/routes/ -run 'Bundle'