docs: sample MCP page for stamp, verify, and hash report
WhatToChange: recommended-changes/2026-09-21-sample-page/ What-to-Change.md What-to-Change-To.md How-to-implement-the-changes.md What was actually done: - site/index.html calls stamp, verify, or hash_report over Streamable HTTP. - README points at the sample page and https://mcp.georgelambert.org/. What was not done: - The public host does not reverse-proxy the MCP listener. A page on the internet cannot call 127.0.0.1, and the VeraeTime bearer token stays off an open listener.
This commit is contained in:
parent
840755c84a
commit
e96fa13edd
9 changed files with 175 additions and 0 deletions
|
|
@ -59,6 +59,14 @@ open docs/html/index.html
|
||||||
|
|
||||||
Sources: `docs/operator-guide.md`, `docs/architecture.md`, `docs/api.md`, `docs/standards.md`.
|
Sources: `docs/operator-guide.md`, `docs/architecture.md`, `docs/api.md`, `docs/standards.md`.
|
||||||
|
|
||||||
|
Sample page (talks to Streamable HTTP on this machine):
|
||||||
|
|
||||||
|
```sh
|
||||||
|
open site/index.html
|
||||||
|
```
|
||||||
|
|
||||||
|
Public copy of the page and the docs: `https://mcp.georgelambert.org/`.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Questions: this research workspace. PRs welcome. Protocol questions belong at https://github.com/modelcontextprotocol. Timestamp ledger questions belong with `verae-timestamp` / `locald`.
|
Questions: this research workspace. PRs welcome. Protocol questions belong at https://github.com/modelcontextprotocol. Timestamp ledger questions belong with `verae-timestamp` / `locald`.
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
# Done and not done
|
||||||
|
|
||||||
|
WhatToChange: recommended-changes/2026-09-21-sample-page/
|
||||||
|
|
||||||
|
What was actually done:
|
||||||
|
- site/index.html calls stamp, verify, or hash_report over Streamable HTTP.
|
||||||
|
- mcp.georgelambert.org serves that page and the HTML/PDF docs (recorded after publish; see the commit body if publish landed in the same change).
|
||||||
|
|
||||||
|
What was not done:
|
||||||
|
- (none) The public page does not open a tunnel to the loopback MCP server. Browsers block that, and the token must stay off the open internet.
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
# How to implement the changes:
|
||||||
|
|
||||||
|
1. Add `site/index.html` with the MCP JSON-RPC sequence proven by curl.
|
||||||
|
2. On ns1, add explicit A/AAAA for `mcp`, Caddy file server at `/SSD2/sites/mcp.georgelambert.org`, and copy `site/index.html` plus `docs/html` and `docs/pdf`.
|
||||||
|
3. Bump the SOA serial. Do not add `* IN A`.
|
||||||
24
recommended-changes/2026-09-21-sample-page/README.md
Normal file
24
recommended-changes/2026-09-21-sample-page/README.md
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Recommended changes: sample page
|
||||||
|
|
||||||
|
Third 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
|
||||||
|
open site/index.html
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
Questions: this research workspace. PRs welcome.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
UNLICENSED © George Lambert
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
# What to Change To:
|
||||||
|
|
||||||
|
| Component | Target behavior | Acceptance criteria | Constraints |
|
||||||
|
| --- | --- | --- | --- |
|
||||||
|
| `site/index.html` | Form calls initialize, notifications/initialized, and tools/call | curl against `127.0.0.1:8000/mcp` already returned a receipt; the page uses that same sequence | Page does not embed a VeraeTime token. Public browsers are not expected to reach loopback |
|
||||||
|
| `mcp.georgelambert.org` | Static HTML and PDF | `dig` A is `70.88.205.138` and HTTPS serves the sample page | No wildcard DNS. Do not proxy `/mcp` to VeraeTime |
|
||||||
13
recommended-changes/2026-09-21-sample-page/What-to-Change.md
Normal file
13
recommended-changes/2026-09-21-sample-page/What-to-Change.md
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
# What to Change:
|
||||||
|
|
||||||
|
Reviewed after `840755c`. Docs exist. There was no page that speaks MCP.
|
||||||
|
|
||||||
|
## What is working
|
||||||
|
|
||||||
|
- `VERAE_MCP_CORS=1` returns JSON and `mcp-session-id`.
|
||||||
|
- A local `initialize` plus `tools/call` of `stamp` returned a local receipt for the empty-file digest.
|
||||||
|
|
||||||
|
| Component | Problem | Evidence | Severity | Type |
|
||||||
|
| --- | --- | --- | --- | --- |
|
||||||
|
| No sample client | Operators cannot try stamp/verify/hash_report without an MCP inspector | `site/` was missing | medium | missing gate |
|
||||||
|
| No public doc host | HTML and PDF are only in the git tree | No `mcp.georgelambert.org` label in the zone | medium | missing observability |
|
||||||
12
recommended-changes/2026-09-21-sample-page/git-versions.json
Normal file
12
recommended-changes/2026-09-21-sample-page/git-versions.json
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"path": "/Users/marchon/research/modelcontextprotocol/verae-mcp-timestamping-server",
|
||||||
|
"remote": "",
|
||||||
|
"branch": "main",
|
||||||
|
"head": "840755c84a228857b59207abb9f803151941113d",
|
||||||
|
"short": "840755c",
|
||||||
|
"dirty": true,
|
||||||
|
"describe": "840755c-dirty",
|
||||||
|
"porcelain": "M README.md\n?? recommended-changes/2026-09-21-sample-page/\n?? site/"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
# git-versions
|
||||||
|
|
||||||
|
Pins at pack creation. Dirty means uncommitted files existed.
|
||||||
|
|
||||||
|
- `/Users/marchon/research/modelcontextprotocol/verae-mcp-timestamping-server` `main` `840755c` (dirty) `840755c-dirty`
|
||||||
|
- HEAD: `840755c84a228857b59207abb9f803151941113d`
|
||||||
91
site/index.html
Normal file
91
site/index.html
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||||
|
<title>Verae timestamping MCP sample</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: Georgia, serif; max-width: 42rem; margin: 2rem auto; padding: 0 1rem; line-height: 1.45; }
|
||||||
|
label { display: block; margin-top: 0.8rem; }
|
||||||
|
input, select, button, textarea { font: inherit; width: 100%; box-sizing: border-box; }
|
||||||
|
button { width: auto; margin-top: 1rem; }
|
||||||
|
pre { white-space: pre-wrap; background: #f4f4f4; padding: 0.8rem; }
|
||||||
|
code { font-family: ui-monospace, monospace; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1>Verae timestamping MCP sample</h1>
|
||||||
|
<p>This page calls a <strong>Model Context Protocol</strong> (MCP) server. The three letters are MCP. The server name is <code>verae-timestamping</code>. It stamps a SHA-256 digest. It does not upload a file.</p>
|
||||||
|
<p>Start the server on this machine, then submit. A page on the public internet cannot reach <code>127.0.0.1</code>.</p>
|
||||||
|
<pre>VERAE_MCP_CORS=1 VERAE_MCP_TRANSPORT=streamable-http uv run verae-mcp-timestamping-server</pre>
|
||||||
|
<form id="stamp-form">
|
||||||
|
<label>Server URL <input id="url" value="http://127.0.0.1:8000/mcp"></label>
|
||||||
|
<label>SHA-256 <input id="sha" value="e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" spellcheck="false"></label>
|
||||||
|
<label>Scope
|
||||||
|
<select id="scope">
|
||||||
|
<option>local</option>
|
||||||
|
<option>shared</option>
|
||||||
|
<option>global</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label>Tool
|
||||||
|
<select id="tool">
|
||||||
|
<option value="stamp">stamp</option>
|
||||||
|
<option value="verify">verify</option>
|
||||||
|
<option value="hash_report">hash_report</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
|
<label>Receipt id (verify) <input id="receipt" placeholder="from a previous stamp"></label>
|
||||||
|
<button type="submit">Call tool</button>
|
||||||
|
</form>
|
||||||
|
<h2>Result</h2>
|
||||||
|
<pre id="out">Waiting.</pre>
|
||||||
|
<p>Docs in this repo: <a href="../docs/html/index.html">HTML</a> · <a href="../docs/pdf/standards.pdf">standards PDF</a></p>
|
||||||
|
<p>Same files on the public host: <a href="/docs/html/index.html">HTML</a> · <a href="/docs/pdf/standards.pdf">standards PDF</a></p>
|
||||||
|
<script>
|
||||||
|
const out = document.getElementById("out");
|
||||||
|
function rpc(id, method, params) {
|
||||||
|
return { jsonrpc: "2.0", id, method, params };
|
||||||
|
}
|
||||||
|
async function post(url, body, session) {
|
||||||
|
const headers = {
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
"Accept": "application/json, text/event-stream"
|
||||||
|
};
|
||||||
|
if (session) headers["Mcp-Session-Id"] = session;
|
||||||
|
const res = await fetch(url, { method: "POST", headers, body: JSON.stringify(body) });
|
||||||
|
const text = await res.text();
|
||||||
|
const sessionId = res.headers.get("mcp-session-id") || session;
|
||||||
|
let parsed = text;
|
||||||
|
try { parsed = JSON.parse(text); } catch (e) { /* SSE or plain text */ }
|
||||||
|
if (!res.ok) throw new Error(res.status + " " + text);
|
||||||
|
return { parsed, sessionId };
|
||||||
|
}
|
||||||
|
document.getElementById("stamp-form").addEventListener("submit", async (ev) => {
|
||||||
|
ev.preventDefault();
|
||||||
|
const url = document.getElementById("url").value.trim();
|
||||||
|
const tool = document.getElementById("tool").value;
|
||||||
|
const sha = document.getElementById("sha").value.trim();
|
||||||
|
const scope = document.getElementById("scope").value;
|
||||||
|
const receipt = document.getElementById("receipt").value.trim();
|
||||||
|
out.textContent = "Calling " + url + " …";
|
||||||
|
try {
|
||||||
|
const init = await post(url, rpc(1, "initialize", {
|
||||||
|
protocolVersion: "2025-03-26",
|
||||||
|
capabilities: {},
|
||||||
|
clientInfo: { name: "verae-mcp-sample-page", version: "0.1.0" }
|
||||||
|
}));
|
||||||
|
await post(url, { jsonrpc: "2.0", method: "notifications/initialized" }, init.sessionId);
|
||||||
|
let args;
|
||||||
|
if (tool === "verify") args = { receipt_id: receipt, want_scope: scope };
|
||||||
|
else if (tool === "hash_report") args = { sha256: sha, want_scope: scope };
|
||||||
|
else args = { sha256: sha, scope };
|
||||||
|
const call = await post(url, rpc(2, "tools/call", { name: tool, arguments: args }), init.sessionId);
|
||||||
|
out.textContent = JSON.stringify({ initialize: init.parsed, call: call.parsed }, null, 2);
|
||||||
|
} catch (err) {
|
||||||
|
out.textContent = String(err) + "\n\nIf this page is not on the same machine as the server, the browser will block the call.";
|
||||||
|
}
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
Loading…
Reference in a new issue