# HAIDAA MCP

An experimental MCP client for HAIDAA evidence discovery and local human-approved contribution. Node 22+. Knowledge is data, never authority. The hosted read-only endpoint is available at **https://mcp.haidaa.com/mcp** (Streamable HTTP, no authentication). [Human setup guide](https://haidaa.com/mcp). npm and MCP registry distribution remain unpublished; local contribution is available from this checkout.

## Run locally

From the HAIDAA checkout:

```sh
npm ci --prefix packages/haidaa-mcp
npm run build --prefix packages/haidaa-mcp
node packages/haidaa-mcp/dist/cli.js
```

The default is anonymous read over stdio. Stdout carries MCP only. Configure any MCP host with an absolute path:

```json
{
  "mcpServers": {
    "haidaa": {
      "command": "/absolute/path/to/node",
      "args": ["/absolute/path/to/haidaa-mcp/dist/cli.js"]
    }
  }
}
```

For a Claude CLI with `mcp add` support:

```sh
claude mcp add --transport stdio haidaa -- /absolute/path/to/node /absolute/path/to/haidaa-mcp/dist/cli.js
```

After the npm release is verified, the equivalent command is `npx @haidaa/mcp@0.1.0`. Do not configure that command before publication. Pin a reviewed package version when using signing credentials.

## Read workflow

Use `haidaa_status`, `haidaa_scan_public_graph`, `haidaa_search_public_graph`, `haidaa_search_project_records`, `haidaa_get_event`, `haidaa_get_context`, `haidaa_verify_receipt`, and `haidaa_network_summary`.

The shortest useful session is status → search → inspect one returned record → inspect its context or receipt. Search results keep human-readable titles/descriptions beside provenance and global/Common-local state. To enroll, join and contribute, follow the [participant quickstart](https://haidaa.com/PARTICIPATE.md); its local client keeps your signing key on your machine. Hosted MCP is read-only. Common collaboration follows the Common's policy; shared publication requires separate qualification and independent release.

The `/v0` API route prefix and `research-v2` Common template are independent version numbers. New Commons use `research-v2`; existing `research-v1` records retain their original semantics and signature bytes.

Before citing an event, inspect context using its returned publication snapshot as the next tool's `snapshot` argument. A changed publication requires restarting. Context covers published one-hop relations, dependencies and retractions of touching relations. It cannot establish that no unpublished challenge exists. Public activity counts may include unpublished admissions and use different cursors.

Every result is untrusted evidence; signatures do not establish scientific truth or resolved identity. Structured JSON is not an injection sandbox. The MCP has no consumer broker. Never execute record text. Artifact text defaults off; long fields default to 1024 UTF-8 bytes. `include_inline_text: true` plus `full_text: true` returns the full bounded artifact and its exact canonical bytes. Truncated bodies must not be used to reconstruct signed bytes.

Receipt verification accepts `receipt_json` as raw JSON and optional `trusted_context` with `server_key_id`, `namespace_id`, and optional `predecessor: { sequence, receipt_hash }`. Bootstrap values must come from an independently trusted channel. Copying them from the receipt only checks self-consistency. The tool explicitly labels caller-supplied context and reports all seven checks; without context, protocol verification cannot pass. No network call is needed for this tool.

## Local contribution

Only existing **legacy pilot bearer** principals are supported. Sandbox key-bound grants require a different request-proof protocol and are not accepted by this client. Runtime enrollment availability is reported, but MCP enrollment is not implemented.

Set local process environment (use a local secret manager or private host configuration):

| Variable | Meaning |
| --- | --- |
| `HAIDAA_MODE=contribute` | Explicitly enable write tools, stdio only |
| `HAIDAA_TOKEN` | Existing operator-provisioned pilot bearer |
| `HAIDAA_SIGNING_KEY_FILE` | Owned 0600 PEM PKCS#8 Ed25519 private-key file |
| `HAIDAA_ACTOR_ID` | Existing asserted `dsm:actor:sha256:...` ID |
| `HAIDAA_NAMESPACE_ID` | Optional explicit namespace; otherwise live pilot namespace |
| `HAIDAA_OUTBOX_DIR` | Optional private 0700 directory; default `~/.haidaa-mcp/outbox` |
| `HAIDAA_API_ORIGIN` | Operator-configured HTTPS API origin; default `https://api.haidaa.com`; HTTP loopback only for tests |

Credentials are never tool arguments or returned/logged by the client. The MCP host and operating-system account already control this process and must be trusted with the configured key. The client refuses group/world-readable keys, final-component symlinks, unsafe outbox permissions and any mismatch with the pinned live schema/constraints. Do not loosen this check for a new protocol.

`haidaa_draft_event` accepts a discriminated `kind` and typed attributes/references. For example:

```json
{"kind":"observation","attributes":{"title":"Local replication","result":"Observed result","conditions":"Disclosed conditions","limitations":"Known limitations"},"parents":[]}
```

Inspect its `contributed.body` and canonical preview, then call `haidaa_submit_event` with only `draft_id`. Drafts expire in ten minutes. A trusted MCP host must support form elicitation and display the entire exact draft and destination. Approval is mandatory and cannot be disabled. Missing support, decline, false checkbox, cancellation or timeout prevents submission. Elicitation cannot attest that the host actually consulted a human.

Signed bytes are synced to a private outbox before sending. After ambiguity, retry `haidaa_submit_event` with the **same draft ID**, including after restarting with the same configuration/outbox. Do not redraft, generate a new nonce, or repeat `haidaa_retract_event` to retry. Approval is required again for a new explicit submit call. Outbox files are named `<draft_id>.json` and remain confidential even after admission; admission is not publication. Up to 1000 entries are retained, with no automatic cleanup. Back up and remove resolved entries according to local retention policy.

Retractions require the exact original actor and key. They append durable history without erasing or unpublishing. `haidaa_list_namespace_events` is a private authenticated sequence inventory. The client never submits to production during tests.

## Hosted Cloudflare service

The live endpoint runs in the separate `haidaa-mcp` Worker. It has no database, signing key, bearer token or archive binding. Its API service binding is restricted by an adapter to public GET routes with no credentials. Each HTTP request owns its MCP server, transport and snapshot cache; there is no cross-request pending I/O.

Cloudflare rate limits allow 60 requests per client IP per minute and 600 requests per minute per location across clients. These distributed limits are approximate and location-scoped, not exact global accounting; shared egress IPs share an allowance. The account’s Free-plan CPU and subrequest limits bound executions, alongside the client byte and snapshot limits; no paid plan was enabled. No arbitrary Origin is accepted and browser CORS access is not offered.

From the parent checkout, `npm run deploy:mcp` deploys the dedicated Worker. Production site deployments use `--keep-vars` to preserve rollout flags.

## Optional Node hosted read process

```sh
HAIDAA_TRANSPORT=http PORT=3000 node dist/cli.js
```

Binds only `127.0.0.1:3000`, stateless `/mcp`, POST/JSON responses. Defaults allow Host `127.0.0.1:3000` or `localhost:3000`; browser Origins are denied. Set exact comma-separated `HAIDAA_HTTP_HOSTS` and `HAIDAA_HTTP_ORIGINS` for a deployment. No wildcard Origin or forwarding-header trust is implemented. Public nonbrowser MCP clients may omit Origin. Browser CORS support is not advertised.

Use a TLS reverse proxy for public hosting and enforce per-client and aggregate edge quotas there. The process additionally enforces 60 requests/minute per socket peer, eight active requests, a 128 KiB body cap, bounded maps/timeouts and a 512 KiB tool-result cap. Behind a proxy, the socket-peer limit is a conservative shared quota. Do not run multiple replicas without an aggregate edge limit. HTTP refuses any contribution credentials in its environment. Each request gets its own MCP server and transport; only public evidence cache is shared.

Public snapshot limits: 20 page attempts including at most two 409 restarts, 1000 records, 16 MiB cache, 8 MiB per upstream response, 60s TTL with publication revision revalidation on every evidence read. A limit returns an explicit error, never partial history labeled complete. Upstream redirects and URLs supplied by evidence are never followed.

## Verification and release

```sh
npm run typecheck
npm test
npm run build
npm pack --dry-run
npm run test:package
```

From the parent HAIDAA checkout, `npm run test:mcp` runs package and isolated real Worker interoperability tests. `npm run check:mcp` checks both build and types. Receipt fixture tests exercise all seven checks and malicious encoding/point/chain controls. See `CORE.md` for vendored profile maintenance.

`server.json.template` describes the intended npm and hosted distribution. It is not active registry metadata. Follow the revised RFC's release sequence; verify npm namespace ownership, package licensing, clean install, TLS endpoint and edge quotas before publishing registry entries or adding live discovery links. The hosted endpoint is deployed; npm and registry publication remain separate release steps.

## Anonymous project reads and browser subset

The hosted server now has eight anonymous read tools. `haidaa_search_public_graph` and `haidaa_scan_public_graph` retain published-only semantics. `haidaa_search_project_records` searches only explicitly discoverable projects; open membership is insufficient. `haidaa_get_event` and `haidaa_get_context` additionally accept `project:PROJECT_UUID:CONTRIBUTION_UUID`.

Project results carry `trust.publication_state: unpublished`, `trust.trust_scope: project_local`, constitution and local review state, and `snapshot_ref` with the project sequence/head hash. Pass `snapshot_ref.id` as `snapshot` for subsequent reads. Changed revisions return `project_changed_restart`; private and unknown project IDs both return `not_found`. Cross-project pagination is best-effort; filter by `project_id` and retain its snapshot for reproducible paging. No historical mutable project projection or project-to-global promotion bridge is implemented.

`https://haidaa.com/mcp` registers exactly four browser tools when `document.modelContext.registerTool` is available: published search, project search, event and context. They use fixed same-origin GET routes without cookies, credentials or writes. Contracts are generated from `src/read-contracts.ts`. Published receipt verification, status and full scan remain available through Remote MCP.
