Metadata-Version: 2.5
Name: hyperspell-mcp
Version: 1.4.0
Summary: Shared MCP tool catalog and backends for the Hyperspell company brain.
Requires-Python: >=3.10
Requires-Dist: httpx<1,>=0.27
Requires-Dist: mcp<3,>=2.3
Requires-Dist: pydantic<3,>=2
Description-Content-Type: text/markdown

# hyperspell-mcp

The single, canonical Model Context Protocol surface for the Hyperspell company brain.

This package owns the **tool catalog** (names, descriptions, annotations, parameter
defaults, compaction) and the **backend seam** that lets the same catalog run over two
transports:

- **Remote** — `register_tools(mcp, InProcessBackend())` mounted as Streamable HTTP at
  `/mcp` on core-api. `InProcessBackend` lives in core-api because it calls the real
  route handlers in-process.
- **Local** — `register_tools(mcp, HttpBackend(...))` run over stdio by the sync daemon.

It deliberately does **not** copy core-api's request models. The tool parameters are
simple primitives; the only shared models are the lightweight response ("lite") models
that results are validated into so compaction is defined exactly once.

See `specs/components/unified-mcp-surface.md` for the full design and the
minimum-maintenance invariants.

Version 1.0.0 requires `mcp>=2.3,<3` (ENG-5290), which serves MCP protocol
`2026-07-28` alongside the older handshake versions. `register_tools` takes an
`mcp.server.mcpserver.MCPServer` (the v2 name for `FastMCP`); the catalog's tools,
schemas and payloads are unchanged. mcp 2 reports only a `ToolError`'s text to the
client, so the catalog turns its caller-facing `ValueError`s (argument checks, the
server's curated 4xx `detail`) into `ToolError`s, and any other exception's text stays on
the server. Hosts still on mcp 1 stay on `hyperspell-mcp<1`.

Version 0.28.0 removes the old context tree (ENG-4858): the `generate_brain`,
`get_brain_config`, `set_brain_config` and `reset_brain_config` tools (all disabled
since 0.17.0) and their `/context-documents/*` backend calls, and the daemon-synced
`~/.hyperspell` reader with its `hyperbrain://context` resources. `register_context_tools`
now registers nothing. It, `CONTEXT_TOOL_NAMES`, the `ContextSource` protocol and
`grep_texts` / `index_from_paths` / `sections_from_paths` stay only because core-api
still imports them. Live pages (`list_pages`, `get_page`) are unchanged.

Version 0.27.0 describes the `errors` of `ask` and `search` in their output schema: the
notices there should change what an agent does next, and a source with no results and no
notice returned nothing for this query (name it in `sources` to learn why). The `effort` description no longer promises a
downgrade notice that the server stopped sending when the effort flags were removed. Core
stops sending per-source "no results" notices for a search that names no `sources`.

Version 0.26.0 returns `provenance.entities` from `ask` and `search` and omits the
provenance fields Core has deprecated: `steps` (the calls in `turns`), `sources` (the
returned `documents`), `failed_sources` (now the per-source notices in `errors`, each
with a `source` field), `entity_resolutions` (the possible matches in `entities`), and
the `iteration` of each turn and tool call (its position). Core still returns them to REST callers until a future release removes them.
The tool descriptions explain the two kinds of `entities` item. Deploy Core's
`entities` before publishing, then update the consumer locks.

Version 0.25.0 adds `rate_answer`: an agent rates an `ask` or `search` result by its
`query_id`, with a `score` from -1 (bad) to +1 (good) and an optional `comment` of at
most 2,000 characters saying what was wrong or right. Backed by
`POST /evaluate/query/{query_id}`; a user-tied credential rates only that user's
queries. A write needing `memories:write`, advertised only by adapters that implement
it. Merge and deploy the matching hosted/local adapters before publishing, then update
the consumer locks.

Version 0.24.0 adds `list_pages` and `get_page`: the live pages (the continuously
maintained article about one entity) this identity can read. `list_pages` returns
`name`, `id`, `url`, `entity_type` and `last_updated` per page, offset-paged;
`get_page` returns one page's article as markdown (the default) or as the Hyperdoc
tree, with its metadata and the citations the caller can read; a page over 500 KB
comes back in slices joined by `next_cursor`. Both are read-only and
advertised only by adapters that implement them. Merge and deploy the matching
hosted/local adapters before publishing, then update the consumer locks.

Version 0.23.2 relays Core's own error envelope on the HTTP backend's write path. A
write the route refused already surfaced FastAPI's `detail` (a share policy); Core's
exceptions answer `{error, message}` instead, so a credential without `memories:write`
reached the caller as httpx's bare `403 Forbidden for url`. It now surfaces as
`403: Missing scopes: ['memories:write'] (WrongScopes)`: the message, then the public
error code, so a caller (the activity-capture mod) can tell a credential it must replace
from a policy it must follow. Upgrade the local CLI dependency to activate it; the hosted
server's in-process backend is unaffected.

Version 0.23.1 requests query provenance at every effort. `provenance.turns` keeps
each native provider summary beside that turn's calls and observed outcomes;
unavailable summaries are null and calls may be empty. This records existing model
calls without adding calls or enabling thinking at lower efforts. Deploy Core's
all-effort turn collection first; older servers may return no lower-effort trace,
which clients retain without fabricating summaries. Correctable HTTP 422 query
rejections retain an empty turns envelope.

The hosted Core server additionally offers `list_sharing_requests` and
`decide_sharing_request` to personal credentials. These reuse the dashboard's
sharing decisions without requiring the UI; they are not tools in this package's
local stdio catalog. The updated hyperbrain CLI offers the same operations.
See [agent sharing review](../../specs/api/memory-sharing.md).

The candidate catalog supports `expected_revision` on `log_activity` (with an
explicit `entry_id`) and `update_memory`. Preserve the `revision` returned by a
write or an authorized stored-body read; `absent` means create only. Conflicts
require reading the newer record and resolving conflicting decisions deliberately.
A timeout may have committed, so read the same record before another write.
Never remove the guard or change the entry ID to bypass a conflict.

Guarded HTTP calls use explicit conditional routes; older servers reject them.
Older adapters must explicitly accept the precondition or the catalog refuses
before writing. Unconditional calls remain compatible. Deploy the server first,
publish a reviewed package version and update consumer pins, then adopt the guard
in the Activity plugin. This source change does not upgrade installed clients.

The real Core integration test is reproducible from `apps/core`:

```sh
PYTHONPATH=../../packages/hyperspell-mcp/src HYPERSPELL_MCP_CANDIDATE=1 \
  task test FULL=true -- tests/api/test_mcp_revision_workflow.py
```

It drives hosted MCP and the HTTP adapter through real application authorization,
Postgres transactions and reads. It injects a lost response after commit and checks
that recovery preserves newer decisions. Reconciliation text is scripted: this is
protocol evidence, not an evaluation of an agent's reasoning quality. Core CI runs
this candidate process separately from the normal published-catalog suite.

Version 0.22.0 adds `log_activity`: an agent records what it just did as one dated
activity entry (a Vault memory with `kind = agent_activity` metadata, plus `agent` and
`workstream` when given). `share` defaults to `suggest`: the entry stays private to the
user and asks them, in their Inbox, whether to share it with the company (`personal`
and `shared` are the other values). That is the user-tied case (OAuth, a personal key);
with a user-less workspace key every write is the company's already, so `suggest` and
`shared` write it as such and `personal` is refused (422). `entry_id` revises one entry
in place instead of adding another (the caller chooses the unit of work; the
activity plugin keeps a local remote pointer per repository, branch and server,
creating a fresh UUID id when a read returns an ambiguous not-found answer); it is written under the
`activity-` resource-id namespace, so the tool can never revise a memory saved with
`remember`. Advertised only by adapters that implement it.
Merge and deploy the matching hosted/local adapters before publishing, then update the
consumer locks.

Version 0.23.0 rejects unknown arguments on the live-query tools before reaching
the backend. GET filters belong in `query`, and read-via-POST arguments in `body`;
`params` is rejected instead of silently returning unfiltered data. Upgrade both
the hosted Core and local CLI dependency to activate this validation.

Version 0.20.0 updates the `recency_half_life_days` description for `ask` and
`search`: age now reduces a result's score by at most 50% (one half-life retains 75%,
two retain 62.5%), matching core-api's ranking. The parameter, its type, and the
omitted-value behavior are unchanged. Deploy core-api first so the description never
promises a stronger preference than the server applies.

Version 0.19.0 adds `update_memory`: replace the text and/or title of a Vault memory
(one saved with `remember`) in place, keeping its `resource_id`. It is destructive but
idempotent, Vault-only (a synced document's next sync would overwrite an edit), and
advertised only by adapters that implement it. Merge and deploy the matching
hosted/local adapters before publishing, then update the consumer locks.

Version 0.18.0 adds `search_entities`, `get_entity`, and `get_entity_mentions`.
Search matches names (an empty query lists one page); it does not run an embedding
or a model. Reads preserve the caller's document access, source references,
possible-match labels and pagination. HTTP consumers reject entity responses from
servers that do not advertise the permission-aware entity contract. Merge and deploy
the matching hosted/local adapters before publishing this package, then update the
consumer locks. This package release alone cannot update an installed CLI adapter.

Version 0.17.0 additionally disables `brain_status`, `list_context`, `read_context`,
and `grep_context`, including cached calls and local `hyperbrain://context` resources.
Its 12 enabled tools query or manage indexed sources; MCP instructions lead with
`ask`, `search`, and `get_memory`, not generated summaries. Stored summaries,
filesystem helpers, REST, ordinary CLI, and daemon behavior are unchanged.
Local clients need the updated CLI release (0.5.12 or newer); a server deployment
alone cannot change an already-installed stdio server.

Version 0.16.0 also disables app integration configuration discovery and brain-config
reads over MCP, alongside the generation, configuration changes, and connection
revocation disabled in 0.15.0. The
implementations and backend protocol remain intact for a deliberate future re-enable,
but discovery omits the tools and cached calls fail. `list_connections` remains
available for the caller's actual connected accounts. Core and CLI adapters enforce the same policy even when installed
with earlier catalogs; after publication, update their dependency pins/locks and remove only the
temporary fallback policy copies, not the retained implementations. Direct authorized
REST and CLI administration is unchanged.

Version 0.14.0 adds `get_memory(cursor=None, response_profile="api")` to the backend
protocol. The catalog selects the MCP profile, and `HttpBackend` forwards the cursor
and documented profile header. Slack/Teams point reads on a supporting Core server
return authorized pages of indexed chunks (up to 16 chunks and 32 KiB of JSON), with
`body_status`, `notices`, and an optional `next_cursor`. Pass that cursor to the next
`get_memory` call; an empty page may still have a continuation. Responses never include
full channel history, and permissions are rechecked for every page. Other providers
keep their existing read behavior. Older custom adapters remain callable without a
cursor; attempts to continue through an adapter without cursor support fail explicitly.

Version 0.13.0 added `query(response_profile="api")` to the backend protocol. The catalog
selects `mcp` for ask/search; `HttpBackend` forwards it in the documented
`X-Hyperspell-Response-Profile` request header. Direct backend callers keep the API
default. Query date bounds (`after`/`before`) also ship in 0.13.0; listing response
profiles have been available since 0.11.0.

Custom adapters must accept and honor or forward the profile, including adapters with
`**kwargs`. Older adapters that cannot accept it remain callable with their legacy
budgets; upgrading only the catalog does not activate MCP limits on those adapters.
Hosted Core queries already select the MCP profile.

Compact query results preserve supplied `excerpt`/`omitted` body statuses and limitation
errors. Full/preview or unknown statuses are dropped when the body is removed, because
highlights can be generated summaries. Compact listing stubs contain no source content,
so a supplied status becomes `omitted`; legacy absent statuses stay absent. `full=true`
preserves the server representation and status. Automatic full bodies and bounded
explicit reads for other providers or body-enabled listings are not active.

Version 1.3.0 adds `body_mode` to `ask` and `search`:

- `excerpt` (the default for both) is the compact shape above, plus each document's
  source `url` when the server sends one.
- `none` returns references only: `title`, `source`, `score`, `resource_id`,
  `document_date`, `url` and `body_status: "omitted"`. The answer is unchanged. Agents that act on the answer
  should prefer it, because excerpts are most of an `ask` payload.
- `auto` is the server representation, the same as `full=true`; `full=true` wins.

The catalog forwards the mode to an adapter whose `query` accepts `body_mode`, so the
server withholds the text, and it applies the same projection itself for one that does
not. `HttpBackend` sends it in the request body; an older server ignores it.

1.3.0 also adds `get_citations(query_id)`: a past `ask`/`search` result's citations
with their passages, read back from the saved query (`GET /evaluate/query/{id}`) and
re-checked against current access, without running the query again. It is registered
only for an adapter that implements `get_query`. Append `CITATIONS_INSTRUCTIONS` to a
server's instructions when it registers the tool.

For guarded activity capture, use `log_activity_if_current`, which requires
`entry_id` and `expected_revision` and shares the activity writer. Older MCP
argument parsers can silently discard an unknown `expected_revision` on the old
`log_activity` name; the distinct name makes an old catalog reject before writing.
It is exposed only when the adapter explicitly supports the precondition. Never
fall back to `log_activity` after refusal or missing-tool errors.

Core and the CLI install this package from PyPI. Merge the matching backend adapters
before publishing a new version, then update both consumers' dependency pins/locks to
activate the catalog. Older API servers do not enforce response limits; clients must not infer
a bounded response from the package version alone.

Version 1.1.0 adds `index_website` and `website_index_status`. They start a
bounded, one-time public website crawl and report that caller's latest crawl,
including partial failures and the page limit. They require the matching Core
website endpoints; deploy those adapters before publishing, then update Core
and CLI dependency pins/locks. The hosted surface additionally hides these
tools when crawling is unavailable for the caller's region or credential.

Website management adds `list_websites` (which includes each website's refresh
schedule) and `set_website_refresh_schedule`. `index_website` re-crawls a saved
site with its saved bounds when `max_depth` and `limit` are omitted; schedule saves
also use the saved crawl bounds unless overridden. Schedules use `never`, `daily`, or `weekly` with an IANA timezone and must
only be enabled at the user's explicit request. New websites default to company
sharing where app policy permits it; `index_website` accepts `share="personal"` for private pages. Refresh
preserves the saved visibility choice.
Saving does not start a crawl; disabling retains indexed pages. All tools use
the same authenticated Core routes as the dashboard and CLI.

`get_user` returns the signed-in user and, without a `query`, the apps the caller can
target (including the active one); `use_app` pins one of them. With a `query` it looks
up a person and does not list apps.

Listing a website's pages and cancelling a running crawl are not MCP tools (1.2.0
removed `website_pages` and `cancel_website_crawl`); use the dashboard,
`hyperbrain websites pages|cancel`, or the Core REST routes.

`remove_website` previews deletion by default; use `confirm=true` after explicit
approval for that website. It stops automatic refresh and deletes up to 100 owned
pages per call. If a crawl is running, wait for `website_index_status` to report
it stopped; repeat confirmed removal while `remaining` is positive. Other users'
pages are preserved.
