diff --git a/README.md b/README.md index 7052c48..667e6aa 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,15 @@ Global stamp (central VeraeTime). Token lives at `~/.verae/auth.properties` (mod Do not send file bytes to `stamp`. Local receipts persist at `~/.verae/mcp-timestamp-local.json` (`VERAE_LEDGER_PATH`). +Printed and HTML copies of the operator guide, architecture, tool API, and standards: + +```sh +bash scripts/build-docs.sh +open docs/html/index.html +``` + +Sources: `docs/operator-guide.md`, `docs/architecture.md`, `docs/api.md`, `docs/standards.md`. + ## Contributing Questions: this research workspace. PRs welcome. Protocol questions belong at https://github.com/modelcontextprotocol. Timestamp ledger questions belong with `verae-timestamp` / `locald`. diff --git a/docs/api.md b/docs/api.md new file mode 100644 index 0000000..9e8411d --- /dev/null +++ b/docs/api.md @@ -0,0 +1,63 @@ +# Tool API + +Server name: `verae-timestamping`. + +## stamp + +| Argument | Default | Meaning | +| --- | --- | --- | +| `sha256` | required | 64 hex characters | +| `scope` | `local` | `local`, `shared`, or `global` | +| `cube_id` | empty | Stored on the local receipt. Sent only as a local field on the global receipt object; the VeraeTime POST body is the digest | +| `org_id` | empty | Local idempotency key. Ignored by the global client | + +Returns `{ "receipts": [ Receipt ] }`. + +Receipt fields: `receipt_id`, `hash`, `scope`, `cube_id`, `org_id`, `created_at`, `merkle`, `restamp_of`. + +## verify + +| Argument | Default | Meaning | +| --- | --- | --- | +| `receipt_id` | required | Local id or VeraeTime certificate / job id | +| `want_scope` | empty | `global` forces VeraeTime. A weaker scope fails closed when the stored scope ranks lower | + +Returns `{ "valid": bool, "error": string or null, "receipt": Receipt or null }`. + +Scope rank: local < shared < global. + +## hash_report + +| Argument | Default | Meaning | +| --- | --- | --- | +| `sha256` | required | Digest to list | +| `org_id` | empty | Filter when set | +| `want_scope` | empty | Drop receipts below this rank | + +Returns `{ "receipts": [ ... ] }` from the local file. Global-only certificates are not listed. + +## Environment + +| Name | Default | +| --- | --- | +| `VERAE_GLOBAL_URL` | `https://api.veraetime.net` | +| `VERAE_GLOBAL_TOKEN` | empty (then auth.properties) | +| `VERAE_AUTH_PROPERTIES` | `~/.verae/auth.properties` | +| `VERAE_LEDGER_PATH` | `~/.verae/mcp-timestamp-local.json` | +| `VERAE_MCP_TRANSPORT` | `stdio` | +| `VERAE_MCP_HOST` | `127.0.0.1` | +| `VERAE_MCP_PORT` | `8000` | +| `VERAE_MCP_CORS` | unset. `1` adds CORS for the sample page | + +## Client config (stdio) + +```json +{ + "mcpServers": { + "verae-timestamping": { + "command": "uv", + "args": ["run", "--directory", "/Users/marchon/research/modelcontextprotocol/verae-mcp-timestamping-server", "verae-mcp-timestamping-server"] + } + } +} +``` diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..89d6e4d --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,26 @@ +# Architecture + +## Flow + +1. An MCP client calls `stamp`, `verify`, or `hash_report`. +2. `RoutingLedger` sends `scope=global` to `GlobalLedger` and every other scope to `FileLedger`. +3. `FileLedger` keeps an idempotent JSON list. The same digest, org, and scope returns the existing receipt. +4. `GlobalClient` posts the digest to VeraeTime and, when the body has no certificate yet, polls `/api/status/{jobId}`. +5. `verify` with `want_scope=global` posts `{ "certificate": "" }` to `/api/verify`. Otherwise it checks the local file first, then VeraeTime. + +## Modules + +| Module | Role | +| --- | --- | +| `server.py` | MCP tools and process entry | +| `ledger.py` | Receipt model, memory ledger, file ledger, router | +| `global_client.py` | VeraeTime HTTPS client; blocking I/O runs in a worker thread | +| `nats_ledger.py` | Optional leaf `locald` client | + +## Idempotency + +Local stamps are idempotent per digest + org + scope. Global stamps send `restamp: false` and `wait: true`. VeraeTime may return the same certificate for the same digest. This server does not cache global receipts in the local file. + +## What this process is not + +It does not replace `verae-timestamp` / `locald`. It does not write JetStream. It does not store document bytes. Memory ledger exists for tests and for `mcp dev` when a caller injects it. `main()` always builds `RoutingLedger`. diff --git a/docs/html/api.html b/docs/html/api.html new file mode 100644 index 0000000..c8a6f1e --- /dev/null +++ b/docs/html/api.html @@ -0,0 +1,258 @@ + + + + + + + api + + + + +
+

api

+
+

Tool API

+

Server name: verae-timestamping.

+

stamp

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ArgumentDefaultMeaning
sha256required64 hex characters
scopelocallocal, shared, or global
cube_idemptyStored on the local receipt. Sent only as a local field on the +global receipt object; the VeraeTime POST body is the digest
org_idemptyLocal idempotency key. Ignored by the global client
+

Returns { "receipts": [ Receipt ] }.

+

Receipt fields: receipt_id, hash, +scope, cube_id, org_id, +created_at, merkle, +restamp_of.

+

verify

+ +++++ + + + + + + + + + + + + + + + + + + + +
ArgumentDefaultMeaning
receipt_idrequiredLocal id or VeraeTime certificate / job id
want_scopeemptyglobal forces VeraeTime. A weaker scope fails closed +when the stored scope ranks lower
+

Returns +{ "valid": bool, "error": string or null, "receipt": Receipt or null }.

+

Scope rank: local < shared < global.

+

hash_report

+ + + + + + + + + + + + + + + + + + + + + + + + + +
ArgumentDefaultMeaning
sha256requiredDigest to list
org_idemptyFilter when set
want_scopeemptyDrop receipts below this rank
+

Returns { "receipts": [ ... ] } from the local file. +Global-only certificates are not listed.

+

Environment

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameDefault
VERAE_GLOBAL_URLhttps://api.veraetime.net
VERAE_GLOBAL_TOKENempty (then auth.properties)
VERAE_AUTH_PROPERTIES~/.verae/auth.properties
VERAE_LEDGER_PATH~/.verae/mcp-timestamp-local.json
VERAE_MCP_TRANSPORTstdio
VERAE_MCP_HOST127.0.0.1
VERAE_MCP_PORT8000
VERAE_MCP_CORSunset. 1 adds CORS for the sample page
+

Client config (stdio)

+
{
+  "mcpServers": {
+    "verae-timestamping": {
+      "command": "uv",
+      "args": ["run", "--directory", "/Users/marchon/research/modelcontextprotocol/verae-mcp-timestamping-server", "verae-mcp-timestamping-server"]
+    }
+  }
+}
+ + diff --git a/docs/html/architecture.html b/docs/html/architecture.html new file mode 100644 index 0000000..06b2e8b --- /dev/null +++ b/docs/html/architecture.html @@ -0,0 +1,95 @@ + + + + + + + architecture + + + + +
+

architecture

+
+

Architecture

+

Flow

+
    +
  1. An MCP client calls stamp, verify, or +hash_report.
  2. +
  3. RoutingLedger sends scope=global to +GlobalLedger and every other scope to +FileLedger.
  4. +
  5. FileLedger keeps an idempotent JSON list. The same +digest, org, and scope returns the existing receipt.
  6. +
  7. GlobalClient posts the digest to VeraeTime and, when +the body has no certificate yet, polls +/api/status/{jobId}.
  8. +
  9. verify with want_scope=global posts +{ "certificate": "<id>" } to +/api/verify. Otherwise it checks the local file first, then +VeraeTime.
  10. +
+

Modules

+ ++++ + + + + + + + + + + + + + + + + + + + + + + + + +
ModuleRole
server.pyMCP tools and process entry
ledger.pyReceipt model, memory ledger, file ledger, router
global_client.pyVeraeTime HTTPS client; blocking I/O runs in a worker thread
nats_ledger.pyOptional leaf locald client
+

Idempotency

+

Local stamps are idempotent per digest + org + scope. Global stamps +send restamp: false and wait: true. VeraeTime +may return the same certificate for the same digest. This server does +not cache global receipts in the local file.

+

What this process is not

+

It does not replace verae-timestamp / +locald. It does not write JetStream. It does not store +document bytes. Memory ledger exists for tests and for +mcp dev when a caller injects it. main() +always builds RoutingLedger.

+ + diff --git a/docs/html/index.html b/docs/html/index.html new file mode 100644 index 0000000..a1b7912 --- /dev/null +++ b/docs/html/index.html @@ -0,0 +1,19 @@ + + + + +Verae MCP timestamping server + + + +

Verae MCP timestamping server

+

Hash-only stamps for assistants that speak the Model Context Protocol.

+ + + diff --git a/docs/html/operator-guide.html b/docs/html/operator-guide.html new file mode 100644 index 0000000..677ed10 --- /dev/null +++ b/docs/html/operator-guide.html @@ -0,0 +1,128 @@ + + + + + + + operator-guide + + + + +
+

operator-guide

+
+

Operator guide

+

Install

+
cd /Users/marchon/research/modelcontextprotocol/verae-mcp-timestamping-server
+uv sync --group dev
+

Run stdio

+
uv run verae-mcp-timestamping-server
+

Run Streamable HTTP

+
VERAE_MCP_TRANSPORT=streamable-http uv run verae-mcp-timestamping-server
+

The listener is http://127.0.0.1:8000/mcp.

+

For the sample page on another origin:

+
VERAE_MCP_CORS=1 VERAE_MCP_TRANSPORT=streamable-http uv run verae-mcp-timestamping-server
+

A page served from the public internet still cannot call +127.0.0.1 from current browsers. Open +site/index.html on the same machine, or put TLS and an +access control in front of a non-loopback bind you choose yourself.

+

Global token

+
# POST https://api.veraetime.net/auth/login and store access_token=
+# in ~/.verae/auth.properties (mode 600), or:
+export VERAE_GLOBAL_TOKEN=...
+

Tests

+
uv run pytest
+

Stamp traces (live VeraeTime) are a separate script. They write +debug-trace/<YYYY-MM-DDTHHMMSSZ>-<slug>/:

+
uv run python scripts/transaction_log_test.py
+

Build these documents

+
bash scripts/build-docs.sh
+

HTML lands in docs/html/. PDF lands in +docs/pdf/.

+ + diff --git a/docs/html/standards.html b/docs/html/standards.html new file mode 100644 index 0000000..94f16ab --- /dev/null +++ b/docs/html/standards.html @@ -0,0 +1,141 @@ + + + + + + + standards + + + + +
+

standards

+
+

Standards

+

Rules for the Verae MCP timestamping server.

+

Protocol

+

This process is an MCP server (Model Context Protocol). Clients are +assistants and inspectors that speak MCP. It is not a REST product API +and not a browser session by itself.

+

Hash only

+ +

Scopes

+ ++++ + + + + + + + + + + + + + + + + + + + + +
ScopeWhere the receipt lives
localJSON file, default +~/.verae/mcp-timestamp-local.json
sharedSame file. There is no separate shared store in this process
globalPOST https://api.veraetime.net/api/timestamp with +wait=true
+

want_scope=global on verify asks VeraeTime. +A local receipt cannot satisfy global.

+

Global history

+

hash_report reads the local file only. VeraeTime is not +queried for a digest history. A global stamp can verify by certificate +id and still be absent from hash_report. An empty list +means “not in the local file,” not “VeraeTime has no record.”

+

Credentials

+ +

NATS

+

NatsLedger is optional and is not the default. It uses +core NATS request/reply:

+ +

Job-complete and receipt notifications stay ephemeral core NATS +(verae.ts.completed, verae.ts.receipt). This +server does not put those events on JetStream.

+

Transports

+ ++++ + + + + + + + + + + + + + + + + +
VERAE_MCP_TRANSPORTBind
unset or stdioParent process stdin/stdout
streamable-httpVERAE_MCP_HOST (default 127.0.0.1) and +VERAE_MCP_PORT (default 8000), path +/mcp
+

Leave the HTTP bind on loopback unless a reverse proxy terminates TLS +and authentication in front of it. Do not publish the VeraeTime bearer +token on an open internet listener.

+

Errors

+

Invalid digests and unknown scopes raise ToolError with +the ValueError text. HTTP failures from VeraeTime surface +as global timestamp HTTP <status>.

+ + diff --git a/docs/html/style.css b/docs/html/style.css new file mode 100644 index 0000000..27f73aa --- /dev/null +++ b/docs/html/style.css @@ -0,0 +1 @@ +body{font-family:Georgia,serif;max-width:42rem;margin:2rem auto;padding:0 1rem;line-height:1.45}code,pre{font-family:ui-monospace,monospace}table{border-collapse:collapse}td,th{border:1px solid #ccc;padding:0.3rem 0.5rem} diff --git a/docs/operator-guide.md b/docs/operator-guide.md new file mode 100644 index 0000000..c8e9cd0 --- /dev/null +++ b/docs/operator-guide.md @@ -0,0 +1,58 @@ +# Operator guide + +## Install + +```sh +cd /Users/marchon/research/modelcontextprotocol/verae-mcp-timestamping-server +uv sync --group dev +``` + +## Run stdio + +```sh +uv run verae-mcp-timestamping-server +``` + +## Run Streamable HTTP + +```sh +VERAE_MCP_TRANSPORT=streamable-http uv run verae-mcp-timestamping-server +``` + +The listener is `http://127.0.0.1:8000/mcp`. + +For the sample page on another origin: + +```sh +VERAE_MCP_CORS=1 VERAE_MCP_TRANSPORT=streamable-http uv run verae-mcp-timestamping-server +``` + +A page served from the public internet still cannot call `127.0.0.1` from current browsers. Open `site/index.html` on the same machine, or put TLS and an access control in front of a non-loopback bind you choose yourself. + +## Global token + +```sh +# POST https://api.veraetime.net/auth/login and store access_token= +# in ~/.verae/auth.properties (mode 600), or: +export VERAE_GLOBAL_TOKEN=... +``` + +## Tests + +```sh +uv run pytest +``` + +Stamp traces (live VeraeTime) are a separate script. They write `debug-trace/-/`: + +```sh +uv run python scripts/transaction_log_test.py +``` + +## Build these documents + +```sh +bash scripts/build-docs.sh +``` + +HTML lands in `docs/html/`. PDF lands in `docs/pdf/`. diff --git a/docs/pdf/api.pdf b/docs/pdf/api.pdf new file mode 100644 index 0000000..b7ce20e Binary files /dev/null and b/docs/pdf/api.pdf differ diff --git a/docs/pdf/architecture.pdf b/docs/pdf/architecture.pdf new file mode 100644 index 0000000..19b61e3 Binary files /dev/null and b/docs/pdf/architecture.pdf differ diff --git a/docs/pdf/operator-guide.pdf b/docs/pdf/operator-guide.pdf new file mode 100644 index 0000000..e262c2f Binary files /dev/null and b/docs/pdf/operator-guide.pdf differ diff --git a/docs/pdf/standards.pdf b/docs/pdf/standards.pdf new file mode 100644 index 0000000..68d9803 Binary files /dev/null and b/docs/pdf/standards.pdf differ diff --git a/docs/standards.md b/docs/standards.md new file mode 100644 index 0000000..6b1db11 --- /dev/null +++ b/docs/standards.md @@ -0,0 +1,56 @@ +# Standards + +Rules for the Verae MCP timestamping server. + +## Protocol + +This process is an MCP server (Model Context Protocol). Clients are assistants and inspectors that speak MCP. It is not a REST product API and not a browser session by itself. + +## Hash only + +- Callers send a SHA-256 digest: 64 lowercase hex characters. A `sha256:` prefix is stripped. +- The server does not read files, does not accept document bytes, and does not hash content. +- Empty input has the well-known digest `e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855`. Callers may stamp that digest. The server does not invent it from a missing file. + +## Scopes + +| Scope | Where the receipt lives | +| --- | --- | +| `local` | JSON file, default `~/.verae/mcp-timestamp-local.json` | +| `shared` | Same file. There is no separate shared store in this process | +| `global` | `POST https://api.veraetime.net/api/timestamp` with `wait=true` | + +`want_scope=global` on `verify` asks VeraeTime. A local receipt cannot satisfy `global`. + +## Global history + +`hash_report` reads the local file only. VeraeTime is not queried for a digest history. A global stamp can verify by certificate id and still be absent from `hash_report`. An empty list means "not in the local file," not "VeraeTime has no record." + +## Credentials + +- Token: `VERAE_GLOBAL_TOKEN`, else `access_token` / `id_token` / `auth_token` in `~/.verae/auth.properties` (`VERAE_AUTH_PROPERTIES` overrides the path). +- Do not commit tokens. `.env` is gitignored. +- The local receipt file is mode `0600` after each save. + +## NATS + +`NatsLedger` is optional and is not the default. It uses core NATS request/reply: + +- `verae.ts.request` +- `verae.ts.verify` +- `verae.hash.report` + +Job-complete and receipt notifications stay ephemeral core NATS (`verae.ts.completed`, `verae.ts.receipt`). This server does not put those events on JetStream. + +## Transports + +| `VERAE_MCP_TRANSPORT` | Bind | +| --- | --- | +| unset or `stdio` | Parent process stdin/stdout | +| `streamable-http` | `VERAE_MCP_HOST` (default `127.0.0.1`) and `VERAE_MCP_PORT` (default `8000`), path `/mcp` | + +Leave the HTTP bind on loopback unless a reverse proxy terminates TLS and authentication in front of it. Do not publish the VeraeTime bearer token on an open internet listener. + +## Errors + +Invalid digests and unknown scopes raise `ToolError` with the `ValueError` text. HTTP failures from VeraeTime surface as `global timestamp HTTP `. diff --git a/recommended-changes/2026-09-21-docs-and-cors/Done-and-not-done.md b/recommended-changes/2026-09-21-docs-and-cors/Done-and-not-done.md new file mode 100644 index 0000000..f832dfa --- /dev/null +++ b/recommended-changes/2026-09-21-docs-and-cors/Done-and-not-done.md @@ -0,0 +1,11 @@ +# Done and not done + +WhatToChange: recommended-changes/2026-09-21-docs-and-cors/ + +What was actually done: +- Markdown, HTML, and PDF for standards, architecture, API, and the operator guide. +- README points at scripts/build-docs.sh. +- VERAE_MCP_CORS=1 enables CORS on the Streamable HTTP app. + +What was not done: +- (none) diff --git a/recommended-changes/2026-09-21-docs-and-cors/How-to-implement-the-changes.md b/recommended-changes/2026-09-21-docs-and-cors/How-to-implement-the-changes.md new file mode 100644 index 0000000..61dd94e --- /dev/null +++ b/recommended-changes/2026-09-21-docs-and-cors/How-to-implement-the-changes.md @@ -0,0 +1,8 @@ +# How to implement the changes: + +Against the pin in git-versions.md (dirty tree). + +1. Add `docs/standards.md`, `docs/architecture.md`, `docs/api.md`, `docs/operator-guide.md`. +2. Add `scripts/build-docs.sh` (pandoc HTML and PDF). +3. Link the build from `README.md`. +4. When `VERAE_MCP_CORS=1`, run uvicorn on `streamable_http_app()` with `CORSMiddleware`. diff --git a/recommended-changes/2026-09-21-docs-and-cors/README.md b/recommended-changes/2026-09-21-docs-and-cors/README.md new file mode 100644 index 0000000..45d0cc6 --- /dev/null +++ b/recommended-changes/2026-09-21-docs-and-cors/README.md @@ -0,0 +1,25 @@ +# Recommended changes: docs and CORS + +Second review, 2026-09-21. Pins are in git-versions.md. + +## Install + +```sh +cd /Users/marchon/research/modelcontextprotocol/verae-mcp-timestamping-server +uv sync --group dev +``` + +## Usage + +```sh +less recommended-changes/2026-09-21-docs-and-cors/What-to-Change.md +bash scripts/build-docs.sh +``` + +## Contributing + +Questions: this research workspace. PRs welcome. + +## License + +UNLICENSED © George Lambert diff --git a/recommended-changes/2026-09-21-docs-and-cors/What-to-Change-To.md b/recommended-changes/2026-09-21-docs-and-cors/What-to-Change-To.md new file mode 100644 index 0000000..e93b51d --- /dev/null +++ b/recommended-changes/2026-09-21-docs-and-cors/What-to-Change-To.md @@ -0,0 +1,6 @@ +# What to Change To: + +| Component | Target behavior | Acceptance criteria | Constraints | +| --- | --- | --- | --- | +| `docs/*.md` plus HTML and PDF | Operator guide, architecture, tool API, and standards | `bash scripts/build-docs.sh` writes four HTML files and four PDFs, each PDF larger than 10 KB | Facts match the code. Do not document JetStream for job-complete | +| `VERAE_MCP_CORS=1` | Starlette CORS allows the sample page to see the session header | Default `mcp.run` path unchanged when the variable is unset | Bind stays `127.0.0.1` unless the operator sets `VERAE_MCP_HOST` | diff --git a/recommended-changes/2026-09-21-docs-and-cors/What-to-Change.md b/recommended-changes/2026-09-21-docs-and-cors/What-to-Change.md new file mode 100644 index 0000000..abf13ec --- /dev/null +++ b/recommended-changes/2026-09-21-docs-and-cors/What-to-Change.md @@ -0,0 +1,13 @@ +# What to Change: + +Reviewed after `60bcefb`. Pytest 14 passed. The server behavior from the first pack is in place. Operators still had only a short README. + +## What is working + +- Hash-only tools, file ledger mode 0600, global HTTP off the event loop. +- `debug-trace/` remains the live stamp record and stays in git. + +| Component | Problem | Evidence | Severity | Type | +| --- | --- | --- | --- | --- | +| README only | No architecture, tool reference, or standards a reader can print | `docs/` did not exist before this pack | high | doc drift | +| Streamable HTTP | Browser sample on another origin cannot read `mcp-session-id` | `server.py` called `mcp.run` with no CORS middleware | medium | missing gate | diff --git a/recommended-changes/2026-09-21-docs-and-cors/git-versions.json b/recommended-changes/2026-09-21-docs-and-cors/git-versions.json new file mode 100644 index 0000000..1e5e1df --- /dev/null +++ b/recommended-changes/2026-09-21-docs-and-cors/git-versions.json @@ -0,0 +1,12 @@ +[ + { + "path": "/Users/marchon/research/modelcontextprotocol/verae-mcp-timestamping-server", + "remote": "", + "branch": "main", + "head": "60bcefbca2dc2c3e776bf54a8d0690a0e3d86e8f", + "short": "60bcefb", + "dirty": true, + "describe": "60bcefb-dirty", + "porcelain": "M README.md\n M src/verae_mcp_timestamping_server/server.py\n?? docs/\n?? scripts/build-docs.sh" + } +] diff --git a/recommended-changes/2026-09-21-docs-and-cors/git-versions.md b/recommended-changes/2026-09-21-docs-and-cors/git-versions.md new file mode 100644 index 0000000..68589ba --- /dev/null +++ b/recommended-changes/2026-09-21-docs-and-cors/git-versions.md @@ -0,0 +1,6 @@ +# git-versions + +Pins at pack creation. Dirty means uncommitted files existed. + +- `/Users/marchon/research/modelcontextprotocol/verae-mcp-timestamping-server` `main` `60bcefb` (dirty) `60bcefb-dirty` + - HEAD: `60bcefbca2dc2c3e776bf54a8d0690a0e3d86e8f` diff --git a/scripts/build-docs.sh b/scripts/build-docs.sh new file mode 100755 index 0000000..8a615cb --- /dev/null +++ b/scripts/build-docs.sh @@ -0,0 +1,40 @@ +#!/bin/sh +# Render docs/*.md to HTML and PDF. Requires pandoc. +set -eu +root=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd) +cd "$root" +mkdir -p docs/html docs/pdf +css='body{font-family:Georgia,serif;max-width:42rem;margin:2rem auto;padding:0 1rem;line-height:1.45}code,pre{font-family:ui-monospace,monospace}table{border-collapse:collapse}td,th{border:1px solid #ccc;padding:0.3rem 0.5rem}' +for name in standards architecture api operator-guide; do + pandoc "docs/${name}.md" \ + --standalone \ + --metadata "title=${name}" \ + -c style.css \ + -o "docs/html/${name}.html" + pandoc "docs/${name}.md" \ + --metadata "title=${name}" \ + -o "docs/pdf/${name}.pdf" +done +printf '%s\n' "$css" > docs/html/style.css +cat > docs/html/index.html <<'EOF' + + + + +Verae MCP timestamping server + + + +

Verae MCP timestamping server

+

Hash-only stamps for assistants that speak the Model Context Protocol.

+ + + +EOF +echo "wrote docs/html and docs/pdf" diff --git a/src/verae_mcp_timestamping_server/server.py b/src/verae_mcp_timestamping_server/server.py index 9d55373..d4fc540 100644 --- a/src/verae_mcp_timestamping_server/server.py +++ b/src/verae_mcp_timestamping_server/server.py @@ -78,6 +78,20 @@ def main() -> None: if transport == "streamable-http": host = os.environ.get("VERAE_MCP_HOST", "127.0.0.1") port = int(os.environ.get("VERAE_MCP_PORT", "8000")) + if os.environ.get("VERAE_MCP_CORS") == "1": + import uvicorn + from starlette.middleware.cors import CORSMiddleware + + app = mcp.streamable_http_app(json_response=True) + app.add_middleware( + CORSMiddleware, + allow_origins=["*"], + allow_methods=["GET", "POST", "DELETE", "OPTIONS"], + allow_headers=["*"], + expose_headers=["mcp-session-id", "Mcp-Session-Id"], + ) + uvicorn.run(app, host=host, port=port) + return mcp.run(transport="streamable-http", host=host, port=port) return mcp.run()