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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/added-public-knowledge-mcp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
- Document Hive's public, read-only Knowledge MCP endpoint and publish the Hive environment variable reference in site navigation.
4 changes: 4 additions & 0 deletions docs/content/hive/adr/0011-knowledge-system.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,3 +43,7 @@ letting community and org knowledge flow down. The trade-off is a deliberately
simple retrieval model: hashed term-frequency vectors and tag/graph edges are
portable and inspectable, but lower quality than model embeddings, and stale or
incorrect facts still need curation rather than blind injection.

## Operator access

For the anonymous, read-only way to let external agents consult public operational facts, see [Public knowledge MCP endpoint](/docs/hive/public-knowledge-mcp).
552 changes: 552 additions & 0 deletions docs/content/hive/env-vars.md

Large diffs are not rendered by default.

185 changes: 185 additions & 0 deletions docs/content/hive/public-knowledge-mcp.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# Public knowledge MCP endpoint

Hive can publish a small, anonymous MCP endpoint for operational knowledge that
other agents can read without joining the hive. Use it when you want a
troubleshooting assistant, local coding agent, or support script to reason from
the hive's accumulated fixes, regressions, and integration notes instead of
only from model training data.

The endpoint is `POST /mcp/knowledge` on the spoke. It is available in
Hive v5.134.0 and later, shipped for
[hivecommons/hive#10615](https://github.com/hivecommons/hive/issues/10615)
by [hivecommons/hive#10621](https://github.com/hivecommons/hive/pull/10621).

> **Note:** this is currently controlled by environment variables. There is no
> dashboard toggle yet; a separate follow-up is tracking that operator UI.

## Turn it on or off

| Variable | Default | Effect |
| --- | --- | --- |
| `HIVE_PUBLIC_KNOWLEDGE` | disabled | Set to `1`, `true`, `yes`, or `on` to enable `POST /mcp/knowledge`. Any other value makes the path return `404`, even to the owner. |
| `HIVE_PUBLIC_KNOWLEDGE_TAGS` | all public-type facts | Optional comma-separated tag allow-list. When set, only facts carrying at least one listed tag are served. |

Hive reads both variables on every request, so an owner can close the surface
instantly by unsetting or changing `HIVE_PUBLIC_KNOWLEDGE`; a spoke restart is
not required.

Example Kubernetes patch:

```sh
kubectl -n hive set env deploy/hive HIVE_PUBLIC_KNOWLEDGE=1
kubectl -n hive set env deploy/hive HIVE_PUBLIC_KNOWLEDGE_TAGS=troubleshooting,linux
```

To close it:

```sh
kubectl -n hive set env deploy/hive HIVE_PUBLIC_KNOWLEDGE-
```

## What public means

Only operational fact types are eligible:

- `pattern`
- `gotcha`
- `regression`
- `test_scaffold`
- `integration`
- `coverage_rule`
- `general`

Ideation and governance facts are never served: `idea`, `vision`,
`constitution`, `requirement`, `constraint`, `stakeholder`, and `decision`.
Requests for a non-public slug return "not found" rather than revealing that a
private fact exists.

Public facts include only `slug`, `title`, `type`, `body`, `tags`, `related`,
and scored `confidence`. Hive strips source links, authors, usage counters,
confidence reasoning, and lifecycle phase. A fact's `related` list is also
filtered to slugs that are public under the same rules.

## MCP protocol and limits

The endpoint speaks MCP protocol `2025-06-18` over Streamable HTTP with plain
JSON responses.

| Limit | Value |
| --- | --- |
| HTTP method | `POST` only |
| Request body | 64 KiB maximum |
| JSON-RPC batching | Not supported |
| Search results | `knowledge_search` returns at most 50 facts |
| Writes | No write tools or mutation path |

The exposed tools are marked with `readOnlyHint: true`.

| Tool | Arguments | Returns |
| --- | --- | --- |
| `knowledge_search` | `query` required; optional `type`, `limit` | JSON `{query, count, results[]}` |
| `knowledge_get` | `slug` required | One public fact, or an MCP tool error saying the fact was not found |
| `knowledge_export` | none | Markdown export of the public base, grouped by type, with `_meta.etag` and `_meta.facts` |

## Connect agents

Replace `https://hive.example.org` with the public URL for your spoke.

### Goose

Add a Streamable HTTP extension to `~/.config/goose/config.yaml`:

```yaml
extensions:
project-hive:
type: streamable_http
uri: https://hive.example.org/mcp/knowledge
enabled: true
```

### Claude Desktop and Claude Code

Claude Code can register the HTTP MCP server directly:

```sh
claude mcp add --transport http project-hive https://hive.example.org/mcp/knowledge
```

For Claude Desktop, add the same URL as an HTTP MCP server in the app's MCP
server configuration and restart the app so it reloads the configuration.

### Copilot CLI

Add the server to `~/.copilot/mcp-config.json`:

```json
{
"mcpServers": {
"project-hive": {
"type": "http",
"url": "https://hive.example.org/mcp/knowledge"
}
}
}
```

### Raw curl

Initialize the MCP session:

```sh
curl -s https://hive.example.org/mcp/knowledge \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-06-18","capabilities":{},"clientInfo":{"name":"curl","version":"1.0.0"}}}'
```

List the tools:

```sh
curl -s https://hive.example.org/mcp/knowledge \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'
```

Search public knowledge:

```sh
curl -s https://hive.example.org/mcp/knowledge \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":3,"method":"tools/call","params":{"name":"knowledge_search","arguments":{"query":"bluetooth regression","limit":5}}}'
```

## Verify it is working

1. With `HIVE_PUBLIC_KNOWLEDGE` unset, `POST /mcp/knowledge` should return
`404`.
2. Set `HIVE_PUBLIC_KNOWLEDGE=1`.
3. Run the `tools/list` curl command above and confirm the three knowledge
tools are present.
4. Run a `knowledge_search` for a tag or phrase you know exists.
5. If `HIVE_PUBLIC_KNOWLEDGE_TAGS` is set, search for an unlisted tag and
confirm those facts are absent.
6. Unset `HIVE_PUBLIC_KNOWLEDGE` and confirm the endpoint returns `404` again
without restarting the spoke.

## Security posture

This surface is anonymous but read-only. It exposes no create, update, delete,
import, promotion, vault, or GitHub write path, and it does not expose source
attribution or author data. Hosted hub front doors can pass `/mcp/knowledge`
through without a browser session, but the spoke still re-checks
`HIVE_PUBLIC_KNOWLEDGE` and returns `404` when the owner switch is off.

Treat the response body as public information. Before enabling the endpoint,
consider tag scoping with `HIVE_PUBLIC_KNOWLEDGE_TAGS`, review which operational
facts should be public, and use a reverse-proxy allow-list if only specific
agent networks should reach the endpoint.

## Related references

- [Environment variable reference](/docs/hive/env-vars)
- [Knowledge system ADR](/docs/hive/adr/0011-knowledge-system)
- [Upstream public knowledge MCP doc](https://github.com/hivecommons/hive/blob/v5/src/docs/public-knowledge-mcp.md)
- [Knowledge system design](https://github.com/hivecommons/hive/blob/v5/src/docs/design/knowledge-system.md)
- [hivecommons/hive#10615](https://github.com/hivecommons/hive/issues/10615)
- [hivecommons/hive#10621](https://github.com/hivecommons/hive/pull/10621)
12 changes: 8 additions & 4 deletions scripts/sync-hive-docs.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,15 @@ describe("rewriteLinkTarget — Case 2: escapes / unsynced -> absolute GitHub UR
);
});

it("sends an in-tree-but-UNSYNCED doc to GitHub (not synced by the script)", () => {
// env-vars.md exists in hive src/docs but is not on the sync allow-list,
// so links to it resolve to a GitHub blob URL rather than a site route.
it("routes the environment variable reference internally now that it is synced", () => {
expect(rewriteLinkTarget("env-vars.md", README)).toBe(
"https://github.com/hivecommons/hive/blob/v5/src/docs/env-vars.md"
"/docs/hive/env-vars"
);
});

it("routes links to local Hive docs overlays internally", () => {
expect(rewriteLinkTarget("public-knowledge-mcp.md", README)).toBe(
"/docs/hive/public-knowledge-mcp"
);
});

Expand Down
17 changes: 14 additions & 3 deletions scripts/sync-hive-docs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const files: Array<{ source: string; target?: string }> = [
{ source: "securing-your-hive.md" },
{ source: "troubleshooting.md" },
{ source: "backup-restore.md", target: "backup-dr.md" },
{ source: "env-vars.md" },
// Third-party integration guide (hivecommons/hive#10171).
{ source: "integration-guide.md" },
{ source: "integrations/work-source-providers.md" },
Expand All @@ -60,6 +61,7 @@ const files: Array<{ source: string; target?: string }> = [
{ source: "adr/0017-podman-quadlet-lifecycle.md" },
];

const localHiveDocs: Array<{ source: string; target?: string }> = [{ source: "public-knowledge-mcp.md" }];

// ---------------------------------------------------------------------------
// Brand scrub
Expand Down Expand Up @@ -115,7 +117,7 @@ const syncedRepoPaths = new Set(files.map(f => `src/docs/${f.source}`));
// e.g. `src/docs/README.md` -> `/docs/hive/readme`,
// `src/docs/adr/0001-...md` -> `/docs/hive/adr/0001-...`.
const repoPathToSiteRoute = new Map<string, string>();
for (const f of files) {
for (const f of [...files, ...localHiveDocs]) {
const repoPath = `src/docs/${f.source}`;
const target = f.target || f.source;
const targetNoExt = target.replace(/\.mdx?$/i, "");
Expand All @@ -126,7 +128,7 @@ for (const f of files) {
// `README.md` is intentionally excluded because it is ambiguous (root README vs
// adr/README); those are only ever matched by exact path.
const basenameToSiteRoute = new Map<string, string>();
for (const f of files) {
for (const f of [...files, ...localHiveDocs]) {
const base = f.source.split("/").pop()!;
if (base.toLowerCase() === "readme.md") continue;
const target = f.target || f.source;
Expand Down Expand Up @@ -217,6 +219,14 @@ function rewriteLinks(content: string, sourceRepoPath: string): string {
// Exported for unit testing (see scripts/sync-hive-docs.test.ts).
export { rewriteLinkTarget, rewriteLinks };

function applyLocalHiveOverlays(content: string, source: string): string {
if (source !== "adr/0011-knowledge-system.md") {
return content;
}

return `${content.trimEnd()}\n\n## Operator access\n\nFor the anonymous, read-only way to let external agents consult public operational facts, see [Public knowledge MCP endpoint](/docs/hive/public-knowledge-mcp).\n`;
}

async function fetchText(url: string): Promise<string> {
const response = await fetch(url);
if (!response.ok) {
Expand All @@ -237,7 +247,8 @@ async function main() {
}
const content = await fetchText(sourceURL);
// Rewrite GitHub-relative links so they resolve on the docs site.
const rewritten = rewriteLinks(content, `src/docs/${file.source}`);
const overlaid = applyLocalHiveOverlays(content, file.source);
const rewritten = rewriteLinks(overlaid, `src/docs/${file.source}`);
fs.mkdirSync(path.dirname(targetPath), { recursive: true });
fs.writeFileSync(
targetPath,
Expand Down
2 changes: 2 additions & 0 deletions src/app/docs/page-map.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ const NAV_STRUCTURE_HIVE: Array<{ title: string; items: NavItem[] }> = [
title: 'Integrating with Hive',
items: [
{ 'Integration guide': 'integration-guide.md' },
{ 'Public knowledge MCP endpoint': 'public-knowledge-mcp.md' },
{ 'Work-source providers': 'integrations/work-source-providers.md' },
{ 'Clanker (Flue-style) interface': 'integrations/clanker-flue.md' },
{ 'Spektacular project inception': 'integrations/spektacular.md' },
Expand Down Expand Up @@ -167,6 +168,7 @@ const NAV_STRUCTURE_HIVE: Array<{ title: string; items: NavItem[] }> = [
{ '0016 CSP script-src scope': 'adr/0016-csp-script-src-scope.md' },
{ '0017 Podman Quadlet lifecycle': 'adr/0017-podman-quadlet-lifecycle.md' },
] },
{ 'Environment variable reference': 'env-vars.md' },
]
}
]
Expand Down
Loading