mcp-context-card 1.0.0 → 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.well-known/fafa +1 -1
- package/CHANGELOG.md +63 -4
- package/README.md +22 -8
- package/dist/bin.d.ts +1 -1
- package/dist/bin.js +50 -9
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/render-card.d.ts +6 -0
- package/dist/render-card.js +70 -12
- package/dist/server.js +6 -2
- package/dist/transport/http.js +1 -1
- package/docs/MECHANISMS.md +1 -1
- package/docs/card-dark.html +52 -26
- package/docs/card-light.html +52 -26
- package/docs/card.html +52 -26
- package/docs/index.html +52 -26
- package/package.json +4 -3
- package/server.json +2 -2
package/.well-known/fafa
CHANGED
|
@@ -9,7 +9,7 @@ agent:
|
|
|
9
9
|
name: "mcp-context-card"
|
|
10
10
|
displayName: "mcp-context-card"
|
|
11
11
|
vendor: "io.github.Wolfe-Jam"
|
|
12
|
-
version: "1.
|
|
12
|
+
version: "1.1.0"
|
|
13
13
|
description: >-
|
|
14
14
|
The essential MCP components for a project's context (AGENTS.md),
|
|
15
15
|
cross-session memory, and identity — a base MCP on its own, or a
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,63 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project. Adheres to [Semantic Versioning](https://semver.org).
|
|
4
4
|
|
|
5
|
+
## 1.1.0
|
|
6
|
+
|
|
7
|
+
The card scans in one screen — AGENTS.md sections collapse by default.
|
|
8
|
+
|
|
9
|
+
Every AGENTS.md section is now its own `<details>`, closed on load: the card
|
|
10
|
+
opens as identity → the section list → memory count → discovery, one screen,
|
|
11
|
+
scannable. Click a section to read it; the section index stays sticky at the
|
|
12
|
+
top so it's always one click away, not a scroll back up.
|
|
13
|
+
|
|
14
|
+
- **Expand all / Collapse all** — a control in the sticky nav, from one
|
|
15
|
+
~16-line inline script (no external resources; the card is still one
|
|
16
|
+
self-contained file). It's a progressive enhancement: the button ships
|
|
17
|
+
`hidden` and the script reveals it, so with JavaScript off every section
|
|
18
|
+
still opens and closes on its own.
|
|
19
|
+
- **`--expanded` / `?expand=all` / `expanded: true`** — the full-page render,
|
|
20
|
+
for a screenshot or a PR. Available on the CLI (`npx mcp-context-card card
|
|
21
|
+
--expanded`), the HTTP transport (`GET /card?expand=all`), and the
|
|
22
|
+
`render_context_card` tool. This path renders `<details open>` server-side —
|
|
23
|
+
no script involved.
|
|
24
|
+
- **A section index link opens its section** — `#section` in the URL, or a
|
|
25
|
+
click in the nav.
|
|
26
|
+
- **Print / save-as-PDF** opens every section first, then restores.
|
|
27
|
+
|
|
28
|
+
No API change to the nine tools. `render_context_card` gains an optional
|
|
29
|
+
`expanded` boolean; everything else is unchanged.
|
|
30
|
+
|
|
31
|
+
## 1.0.1
|
|
32
|
+
|
|
33
|
+
`npx mcp-context-card` now runs — it silently no-op'd when launched through
|
|
34
|
+
`npx` or a global install. Plus: `card` opens your context in a browser at a
|
|
35
|
+
terminal, and Node 20 is supported.
|
|
36
|
+
|
|
37
|
+
Once `npx` works, `npx mcp-context-card card` from a project directory is the
|
|
38
|
+
fastest way to see what an agent actually gets: at a terminal it writes
|
|
39
|
+
`context-card.html` and opens it in your browser — no MCP host, no redirect.
|
|
40
|
+
Piped or redirected output is unchanged (`> card.html`, scripts, CI);
|
|
41
|
+
`--stdout` forces raw HTML from a terminal too.
|
|
42
|
+
|
|
43
|
+
- **Node 20 supported.** `engines` was `>=22` with nothing in the code that
|
|
44
|
+
needed it — it runs identically on Node 20 (verified against the published
|
|
45
|
+
package). No more `npm warn EBADENGINE` on the current LTS. CI now runs the
|
|
46
|
+
full suite on Node 20, 22, and 24 across all three OSes.
|
|
47
|
+
- **`card` at a terminal.** A bare `npx mcp-context-card card` used to dump
|
|
48
|
+
raw HTML at the prompt — noise for anyone who just wanted to look. Now it
|
|
49
|
+
writes a file and opens it; the pipe/redirect path is untouched.
|
|
50
|
+
- **`npx` / global-install entry fixed.** The bin's "am I the entry point?"
|
|
51
|
+
guard compared `import.meta.url` (always resolved) against an unresolved
|
|
52
|
+
`process.argv[1]` — so when `npx`, a global install, or `./node_modules/.bin`
|
|
53
|
+
routed through the bin **symlink**, the check failed and the CLI **silently
|
|
54
|
+
did nothing** (exit 0, no output). `process.argv[1]` is now realpath'd first.
|
|
55
|
+
Direct `node dist/bin.js` was never affected, which is why CI and the client
|
|
56
|
+
conformance passes (which spawn `node <path>`) stayed green. CI now also
|
|
57
|
+
exercises the packaged `npx` entry.
|
|
58
|
+
|
|
59
|
+
No API change. Nine tools, three concerns, two transports, two discovery
|
|
60
|
+
mechanisms — all as 1.0.0.
|
|
61
|
+
|
|
5
62
|
## 1.0.0
|
|
6
63
|
|
|
7
64
|
Stable surface. No functional change from 0.6.2 — this release declares
|
|
@@ -16,10 +73,12 @@ mechanisms** (the Server Card `_meta` block, a self-published
|
|
|
16
73
|
|
|
17
74
|
Verified against three independent clients before the cut:
|
|
18
75
|
|
|
19
|
-
- **Cursor** (3.18.25 / Grok 4.6) — the full behavioural matrix, 10/10
|
|
20
|
-
- **
|
|
21
|
-
|
|
22
|
-
|
|
76
|
+
- **Cursor** (3.18.25 / Grok 4.6) — the full behavioural matrix, **10/10**.
|
|
77
|
+
- **Claude Code protocol pass** — the 10-item matrix run through the
|
|
78
|
+
MCP SDK `Client` + `StdioClientTransport` (the client stack Claude
|
|
79
|
+
Code uses), over stdio. **`CLAUDE CODE PROTOCOL PASS: 10/10`** —
|
|
80
|
+
every check, and `resources/templates/list` issued directly on the
|
|
81
|
+
wire (`[]`, not `-32601`).
|
|
23
82
|
- **`@modelcontextprotocol/inspector`** (2.5.0, the canonical
|
|
24
83
|
conformance tool) — every method answers correctly; `tools/list
|
|
25
84
|
--strict` reports zero schema-portability problems across all nine.
|
package/README.md
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# mcp-context-card
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/mcp-context-card)
|
|
3
4
|
[](https://github.com/Wolfe-Jam/mcp-context-card/actions/workflows/ci.yml)
|
|
4
5
|
[](./LICENSE)
|
|
6
|
+
[](https://glama.ai/mcp/servers/Wolfe-Jam/mcp-context-card)
|
|
5
7
|
|
|
6
8
|
**Get one. Or add it to yours.** The essential MCP server for a project's
|
|
7
9
|
context, memory, and identity — discoverable to any MCP client, and
|
|
@@ -68,13 +70,21 @@ file access, no shell, no search.
|
|
|
68
70
|
|
|
69
71
|
The screenshot at the top of this page is exactly this — the same three
|
|
70
72
|
sources rendered as one self‑contained HTML page: identity, `AGENTS.md`,
|
|
71
|
-
memory, and how a machine fetches it. The view for people:
|
|
72
|
-
drop it in a PR, put it on a status page.
|
|
73
|
+
memory, and how a machine fetches it. The view for people: read it, screenshot
|
|
74
|
+
it, drop it in a PR, put it on a status page.
|
|
75
|
+
|
|
76
|
+
`AGENTS.md` sections are collapsed by default, so the card scans in one screen;
|
|
77
|
+
a sticky index jumps to any section, **Expand all** opens everything. Sections
|
|
78
|
+
toggle natively — the one small inline script only adds the bulk button and the
|
|
79
|
+
print handler. `--expanded` / `?expand=all` renders it fully open, script-free,
|
|
80
|
+
for a screenshot.
|
|
73
81
|
|
|
74
82
|
```
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
npx mcp-context-card card
|
|
83
|
+
npx mcp-context-card card # at a terminal: writes context-card.html and opens it
|
|
84
|
+
npx mcp-context-card card --expanded # every section open
|
|
85
|
+
npx mcp-context-card card > x.html # piped/redirected: raw HTML to stdout
|
|
86
|
+
GET /card # live, on the HTTP transport
|
|
87
|
+
GET /card?expand=all&theme=light&accent=%230066cc
|
|
78
88
|
```
|
|
79
89
|
|
|
80
90
|
Light, dark, or auto; the accent defaults to the AAIF palette and takes any hex.
|
|
@@ -104,12 +114,16 @@ npx agents-md-facts --check # fail if missing or stale (CI, pre-commit)
|
|
|
104
114
|
|
|
105
115
|
### See the card
|
|
106
116
|
|
|
107
|
-
One command, no host, no config:
|
|
117
|
+
One command, no host, no config — from your project directory:
|
|
108
118
|
|
|
109
119
|
```bash
|
|
110
|
-
npx mcp-context-card card
|
|
120
|
+
npx mcp-context-card card
|
|
111
121
|
```
|
|
112
122
|
|
|
123
|
+
At a terminal it writes `context-card.html` and opens it in your browser. Piped
|
|
124
|
+
or redirected (`> card.html`, a script, CI) it writes raw HTML to stdout instead;
|
|
125
|
+
`--stdout` forces that from a terminal too. `--expanded` opens every section.
|
|
126
|
+
|
|
113
127
|
### Wire it into a host
|
|
114
128
|
|
|
115
129
|
Claude Desktop, Cursor, or any stdio host:
|
|
@@ -128,7 +142,7 @@ Claude Desktop, Cursor, or any stdio host:
|
|
|
128
142
|
|
|
129
143
|
`MCP_CONTEXT_CARD_ROOT` points at the directory with your `AGENTS.md`. The
|
|
130
144
|
memory tools work with or without it; identity is optional. Over HTTP instead:
|
|
131
|
-
`PORT=8080 npx mcp-context-card`. Requires Node ≥
|
|
145
|
+
`PORT=8080 npx mcp-context-card`. Requires Node ≥20.
|
|
132
146
|
|
|
133
147
|
If `command: "npx"` fails to spawn (`spawn npx ENOENT` — seen on Cursor, whose
|
|
134
148
|
host process doesn't inherit a shell `PATH`), point `command` at `node` and
|
package/dist/bin.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export interface Launch {
|
|
|
8
8
|
root: string;
|
|
9
9
|
}
|
|
10
10
|
/** what a bare `--help` / `help` prints. */
|
|
11
|
-
export declare const HELP = "mcp-context-card 1.
|
|
11
|
+
export declare const HELP = "mcp-context-card 1.1.0\nServe a project's context (AGENTS.md), memory, and identity over MCP.\n\nUSAGE\n mcp-context-card stdio MCP server \u2014 what an MCP host spawns (default)\n mcp-context-card --http stateless Streamable HTTP on PORT (default 3000)\n mcp-context-card --stdio force stdio even when PORT is set\n mcp-context-card card this dir's context card \u2014 opens it in your browser\n at a terminal; HTML to stdout when piped ( > f.html )\n --theme light|dark --accent #hex\n --expanded (all sections open) --stdout\n mcp-context-card --help this text\n mcp-context-card --version print version\n\nENV\n MCP_CONTEXT_CARD_ROOT read AGENTS.md / project.fafm / .well-known/ from here\n PORT if set, run HTTP instead of stdio\n\nA bare run is an stdio server: it waits for a host to speak JSON-RPC on stdin,\nso it looks idle at a terminal. Try `card` (opens your context in a browser) or `--http`.\nhttps://github.com/Wolfe-Jam/mcp-context-card\n";
|
|
12
12
|
/**
|
|
13
13
|
* Decide how to launch, from argv + env. Pure — so the mode matrix is unit
|
|
14
14
|
* tested without spawning a process.
|
package/dist/bin.js
CHANGED
|
@@ -6,14 +6,18 @@
|
|
|
6
6
|
* mcp-context-card --http → stateless Streamable HTTP on PORT (default 3000)
|
|
7
7
|
* PORT=8080 mcp-context-card → HTTP too (a hosted deploy sets PORT)
|
|
8
8
|
* mcp-context-card --stdio → force stdio even when PORT is set
|
|
9
|
-
* mcp-context-card card →
|
|
10
|
-
*
|
|
9
|
+
* mcp-context-card card → this directory's context card. At a terminal:
|
|
10
|
+
* writes context-card.html and opens it. Piped
|
|
11
|
+
* or redirected: HTML to stdout ( > card.html ).
|
|
12
|
+
* --theme light|dark · --accent #hex
|
|
13
|
+
* --expanded (all sections open) · --stdout
|
|
11
14
|
* mcp-context-card --help → usage
|
|
12
15
|
* mcp-context-card --version → version
|
|
13
16
|
*
|
|
14
17
|
* MCP_CONTEXT_CARD_ROOT=/path/to/project → read AGENTS.md / project.fafm /
|
|
15
18
|
* .well-known/ from there instead of the package's own bundled copies.
|
|
16
19
|
*/
|
|
20
|
+
import { realpathSync } from "node:fs";
|
|
17
21
|
import { resolve } from "node:path";
|
|
18
22
|
import { pathToFileURL } from "node:url";
|
|
19
23
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
@@ -27,8 +31,10 @@ USAGE
|
|
|
27
31
|
mcp-context-card stdio MCP server — what an MCP host spawns (default)
|
|
28
32
|
mcp-context-card --http stateless Streamable HTTP on PORT (default 3000)
|
|
29
33
|
mcp-context-card --stdio force stdio even when PORT is set
|
|
30
|
-
mcp-context-card card
|
|
31
|
-
|
|
34
|
+
mcp-context-card card this dir's context card — opens it in your browser
|
|
35
|
+
at a terminal; HTML to stdout when piped ( > f.html )
|
|
36
|
+
--theme light|dark --accent #hex
|
|
37
|
+
--expanded (all sections open) --stdout
|
|
32
38
|
mcp-context-card --help this text
|
|
33
39
|
mcp-context-card --version print version
|
|
34
40
|
|
|
@@ -37,7 +43,7 @@ ENV
|
|
|
37
43
|
PORT if set, run HTTP instead of stdio
|
|
38
44
|
|
|
39
45
|
A bare run is an stdio server: it waits for a host to speak JSON-RPC on stdin,
|
|
40
|
-
so it looks idle at a terminal. Try \`card\`
|
|
46
|
+
so it looks idle at a terminal. Try \`card\` (opens your context in a browser) or \`--http\`.
|
|
41
47
|
https://github.com/Wolfe-Jam/mcp-context-card
|
|
42
48
|
`;
|
|
43
49
|
/**
|
|
@@ -76,8 +82,21 @@ export function flagValue(argv, flag) {
|
|
|
76
82
|
const i = argv.indexOf(flag);
|
|
77
83
|
return i >= 0 && i + 1 < argv.length ? argv[i + 1] : undefined;
|
|
78
84
|
}
|
|
79
|
-
/**
|
|
80
|
-
|
|
85
|
+
/**
|
|
86
|
+
* Direct run only — importing this module (e.g. from a test) must not launch.
|
|
87
|
+
* `process.argv[1]` can be a bin symlink (`npx`, a global install, `.bin/…`)
|
|
88
|
+
* while `import.meta.url` is always the resolved file, so realpath argv[1]
|
|
89
|
+
* before comparing — otherwise the CLI silently no-ops when run via npx.
|
|
90
|
+
*/
|
|
91
|
+
const entryPath = (() => {
|
|
92
|
+
try {
|
|
93
|
+
return process.argv[1] ? realpathSync(process.argv[1]) : undefined;
|
|
94
|
+
}
|
|
95
|
+
catch {
|
|
96
|
+
return process.argv[1];
|
|
97
|
+
}
|
|
98
|
+
})();
|
|
99
|
+
if (entryPath && import.meta.url === pathToFileURL(entryPath).href) {
|
|
81
100
|
const argv = process.argv.slice(2);
|
|
82
101
|
const { mode, port, root } = resolveLaunch(argv);
|
|
83
102
|
if (mode === "help") {
|
|
@@ -89,10 +108,32 @@ if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href)
|
|
|
89
108
|
else if (mode === "card") {
|
|
90
109
|
const { renderCard, safeAccent } = await import("./render-card.js");
|
|
91
110
|
const theme = flagValue(argv, "--theme");
|
|
92
|
-
|
|
111
|
+
const html = renderCard(root, {
|
|
93
112
|
theme: theme === "light" || theme === "dark" ? theme : "auto",
|
|
94
113
|
accent: safeAccent(flagValue(argv, "--accent")),
|
|
95
|
-
|
|
114
|
+
expanded: argv.includes("--expanded"),
|
|
115
|
+
});
|
|
116
|
+
// Piped / redirected (or --stdout) → raw HTML on stdout, unchanged.
|
|
117
|
+
// A bare run at a terminal → the HTML is noise; write a file and open it.
|
|
118
|
+
if (!process.stdout.isTTY || argv.includes("--stdout")) {
|
|
119
|
+
process.stdout.write(html);
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
const { writeFileSync } = await import("node:fs");
|
|
123
|
+
const { join } = await import("node:path");
|
|
124
|
+
const { spawn } = await import("node:child_process");
|
|
125
|
+
const out = join(process.cwd(), "context-card.html");
|
|
126
|
+
writeFileSync(out, html);
|
|
127
|
+
const opener = process.platform === "darwin"
|
|
128
|
+
? ["open", [out]]
|
|
129
|
+
: process.platform === "win32"
|
|
130
|
+
? ["cmd", ["/c", "start", "", out]]
|
|
131
|
+
: ["xdg-open", [out]];
|
|
132
|
+
spawn(opener[0], opener[1], { stdio: "ignore", detached: true })
|
|
133
|
+
.on("error", () => { })
|
|
134
|
+
.unref();
|
|
135
|
+
process.stderr.write(`${NAME} · wrote ${out} — opening in your browser (--stdout for raw HTML)\n`);
|
|
136
|
+
}
|
|
96
137
|
}
|
|
97
138
|
else if (mode === "http") {
|
|
98
139
|
const { httpApp } = await import("./transport/http.js");
|
package/dist/constants.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Server identity constants, in their own module so any file can import
|
|
2
2
|
* them without pulling in the whole server. */
|
|
3
3
|
export declare const NAME = "mcp-context-card";
|
|
4
|
-
export declare const VERSION = "1.
|
|
4
|
+
export declare const VERSION = "1.1.0";
|
|
5
5
|
export declare const SERVER_CARD_URI = "mcp-context-card://server-card";
|
package/dist/constants.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/** Server identity constants, in their own module so any file can import
|
|
2
2
|
* them without pulling in the whole server. */
|
|
3
3
|
export const NAME = "mcp-context-card";
|
|
4
|
-
export const VERSION = "1.
|
|
4
|
+
export const VERSION = "1.1.0";
|
|
5
5
|
export const SERVER_CARD_URI = "mcp-context-card://server-card";
|
package/dist/render-card.d.ts
CHANGED
|
@@ -3,6 +3,12 @@ export interface CardOptions {
|
|
|
3
3
|
theme?: Theme;
|
|
4
4
|
/** CSS hex colour for the accent. Validated; invalid falls back to AAIF. */
|
|
5
5
|
accent?: string;
|
|
6
|
+
/**
|
|
7
|
+
* Render every AGENTS.md section open. Default: sections collapse to their
|
|
8
|
+
* headings (`<details>`), click one to read it — the card scans in one screen.
|
|
9
|
+
* `expanded` is the whole-page render, for a screenshot or a PR.
|
|
10
|
+
*/
|
|
11
|
+
expanded?: boolean;
|
|
6
12
|
}
|
|
7
13
|
/** AAIF brand orange (aaif.io). The default accent. */
|
|
8
14
|
export declare const AAIF_ACCENT = "#FF702D";
|
package/dist/render-card.js
CHANGED
|
@@ -6,8 +6,9 @@
|
|
|
6
6
|
* or drop into a PR. Same three sources as the Server Card _meta block and
|
|
7
7
|
* ai-catalog; this is the view for people.
|
|
8
8
|
*
|
|
9
|
-
* Self-contained: inline CSS, no
|
|
10
|
-
*
|
|
9
|
+
* Self-contained: inline CSS, no external fonts or resources. The only script
|
|
10
|
+
* is the expand-all / print helper (TOGGLE_SCRIPT) — a progressive enhancement;
|
|
11
|
+
* every section still opens on its own without it. Renders anywhere.
|
|
11
12
|
*/
|
|
12
13
|
import { join } from "node:path";
|
|
13
14
|
import { parseAgentsMd } from "./agents-md.js";
|
|
@@ -47,7 +48,7 @@ body{margin:0;background:var(--bg);color:var(--fg);
|
|
|
47
48
|
font:15px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
|
|
48
49
|
padding:40px 18px}
|
|
49
50
|
.card{max-width:760px;margin:0 auto;background:var(--card);border:1px solid var(--line);
|
|
50
|
-
border-radius:14px;overflow:
|
|
51
|
+
border-radius:14px;overflow:clip;box-shadow:var(--card-shadow)}
|
|
51
52
|
.card>*{padding:26px 30px}
|
|
52
53
|
.top{border-top:4px solid var(--accent);border-bottom:1px solid var(--line)}
|
|
53
54
|
h1{margin:0 0 10px;font-size:1.7rem;letter-spacing:-.02em}
|
|
@@ -58,9 +59,31 @@ section{border-bottom:1px solid var(--line)}
|
|
|
58
59
|
section:last-child{border-bottom:0}
|
|
59
60
|
.label{font-size:.7rem;font-weight:700;letter-spacing:.16em;text-transform:uppercase;
|
|
60
61
|
color:var(--accent);margin:0 0 14px}
|
|
61
|
-
.toc{display:flex;flex-wrap:wrap;gap:6px 14px;margin:0
|
|
62
|
+
.toc{display:flex;flex-wrap:wrap;gap:6px 14px;margin:0;padding:0;list-style:none}
|
|
62
63
|
.toc a{font-size:.82rem;color:var(--muted);text-decoration:none}
|
|
63
64
|
.toc a:hover{color:var(--accent)}
|
|
65
|
+
/* ── collapsible context ─────────────────────────────────────────── */
|
|
66
|
+
.ctx-nav{position:sticky;top:0;z-index:3;background:var(--card);
|
|
67
|
+
margin:0 -30px 16px;padding:11px 30px;border-bottom:1px solid var(--line);
|
|
68
|
+
display:flex;flex-wrap:wrap;align-items:flex-start;gap:8px 16px}
|
|
69
|
+
.xall{margin-left:auto;flex:none;font:inherit;font-size:.76rem;font-weight:600;
|
|
70
|
+
white-space:nowrap;padding:3px 11px;border-radius:20px;border:1px solid var(--line);
|
|
71
|
+
background:var(--chip);color:var(--muted);cursor:pointer}
|
|
72
|
+
.xall:hover{color:var(--accent);border-color:var(--accent)}
|
|
73
|
+
.ctx-preamble{padding-bottom:4px}
|
|
74
|
+
details.ctx-section{border-top:1px solid var(--line)}
|
|
75
|
+
details.ctx-section>summary{cursor:pointer;list-style:none;padding:11px 0;
|
|
76
|
+
font-weight:600;font-size:1rem;letter-spacing:-.01em;display:flex;gap:9px}
|
|
77
|
+
details.ctx-section>summary::-webkit-details-marker{display:none}
|
|
78
|
+
details.ctx-section>summary::before{content:"›";color:var(--accent);font-weight:700;
|
|
79
|
+
transition:transform .15s ease}
|
|
80
|
+
details.ctx-section[open]>summary::before{transform:rotate(90deg)}
|
|
81
|
+
details.ctx-section>.md{padding:0 0 16px}
|
|
82
|
+
@media print{
|
|
83
|
+
.ctx-nav{display:none}
|
|
84
|
+
details.ctx-section:not([open])>.md{display:block!important}
|
|
85
|
+
details.ctx-section>summary::before{content:""}
|
|
86
|
+
}
|
|
64
87
|
.md h1,.md h2,.md h3,.md h4{margin:22px 0 8px;font-size:1rem;letter-spacing:-.01em}
|
|
65
88
|
.md h1{font-size:1.15rem}
|
|
66
89
|
.md p{margin:8px 0}
|
|
@@ -107,21 +130,28 @@ export function renderCard(root, opts = {}) {
|
|
|
107
130
|
]
|
|
108
131
|
.filter(Boolean)
|
|
109
132
|
.join("");
|
|
110
|
-
// CONTEXT —
|
|
133
|
+
// CONTEXT — one <details> per AGENTS.md section, collapsed by default (the card
|
|
134
|
+
// scans in one screen); `expanded` renders them all open. The "# AGENTS.md"
|
|
135
|
+
// top-level heading is dropped; its intro rides above the sections.
|
|
111
136
|
const bodySections = agents?.sections.filter((s) => s.level > 1) ?? [];
|
|
112
137
|
const toc = bodySections.length
|
|
113
138
|
? `<ul class="toc">${bodySections
|
|
114
139
|
.map((s) => `<li><a href="#${slug(s.heading)}">${escapeHtml(s.heading)}</a></li>`)
|
|
115
140
|
.join("")}</ul>`
|
|
116
141
|
: "";
|
|
142
|
+
const preamble = [agents?.preamble, agents?.sections.find((s) => s.level === 1)?.body ?? ""]
|
|
143
|
+
.filter(Boolean)
|
|
144
|
+
.join("\n\n");
|
|
145
|
+
const openAttr = opts.expanded ? " open" : "";
|
|
146
|
+
const sections = bodySections
|
|
147
|
+
.map((s) => `<details class="ctx-section"${openAttr} id="${slug(s.heading)}"><summary>${escapeHtml(s.heading)}</summary><div class="md">${renderMarkdown(s.body)}</div></details>`)
|
|
148
|
+
.join("");
|
|
117
149
|
const contextBody = agents
|
|
118
|
-
?
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
.filter(Boolean)
|
|
124
|
-
.join("\n\n"))}</div>`
|
|
150
|
+
? `<div class="ctx-nav">${toc}${bodySections.length
|
|
151
|
+
? `<button type="button" class="xall" hidden>${opts.expanded ? "Collapse all" : "Expand all"}</button>`
|
|
152
|
+
: ""}</div>
|
|
153
|
+
${preamble ? `<div class="ctx-preamble md">${renderMarkdown(preamble)}</div>` : ""}
|
|
154
|
+
<div class="ctx-body">${sections}</div>`
|
|
125
155
|
: `<p class="none">No AGENTS.md in this project.</p>`;
|
|
126
156
|
// MEMORY
|
|
127
157
|
const memoryBody = mem.facts.length
|
|
@@ -174,7 +204,35 @@ export function renderCard(root, opts = {}) {
|
|
|
174
204
|
</section>
|
|
175
205
|
<div class="foot">${escapeHtml(name)} · context card</div>
|
|
176
206
|
</main>
|
|
207
|
+
${bodySections.length ? TOGGLE_SCRIPT : ""}
|
|
177
208
|
</body>
|
|
178
209
|
</html>
|
|
179
210
|
`;
|
|
180
211
|
}
|
|
212
|
+
/**
|
|
213
|
+
* Expand-all / Collapse-all. The only script in the card — a progressive
|
|
214
|
+
* enhancement: with it off, every section still opens and closes on its own
|
|
215
|
+
* (native `<details>`), just without the bulk button. It also opens every
|
|
216
|
+
* section for printing, since browsers don't agree on whether a closed
|
|
217
|
+
* `<details>` prints its content.
|
|
218
|
+
*/
|
|
219
|
+
const TOGGLE_SCRIPT = `<script>
|
|
220
|
+
(function(){
|
|
221
|
+
var btn=document.querySelector(".xall");
|
|
222
|
+
var secs=[].slice.call(document.querySelectorAll("details.ctx-section"));
|
|
223
|
+
if(!btn||!secs.length)return;
|
|
224
|
+
var sync=function(){btn.textContent=secs.every(function(d){return d.open})?"Collapse all":"Expand all"};
|
|
225
|
+
btn.hidden=false;
|
|
226
|
+
btn.addEventListener("click",function(){
|
|
227
|
+
var open=!secs.every(function(d){return d.open});
|
|
228
|
+
secs.forEach(function(d){d.open=open});sync();
|
|
229
|
+
});
|
|
230
|
+
secs.forEach(function(d){d.addEventListener("toggle",sync)});
|
|
231
|
+
var openHash=function(){var d=document.getElementById(location.hash.slice(1));if(d&&d.tagName==="DETAILS")d.open=true};
|
|
232
|
+
addEventListener("hashchange",openHash);openHash();
|
|
233
|
+
var pre=[];
|
|
234
|
+
addEventListener("beforeprint",function(){pre=secs.map(function(d){return d.open});secs.forEach(function(d){d.open=true})});
|
|
235
|
+
addEventListener("afterprint",function(){secs.forEach(function(d,i){d.open=pre[i]});sync()});
|
|
236
|
+
sync();
|
|
237
|
+
})();
|
|
238
|
+
</script>`;
|
package/dist/server.js
CHANGED
|
@@ -161,12 +161,13 @@ export function createServer(root = ROOT) {
|
|
|
161
161
|
},
|
|
162
162
|
{
|
|
163
163
|
name: "render_context_card",
|
|
164
|
-
description: "Render the whole card — identity, AGENTS.md, memory, discovery — as one self-contained HTML page a person can read or screenshot. Also served at GET /card over the HTTP transport.",
|
|
164
|
+
description: "Render the whole card — identity, AGENTS.md, memory, discovery — as one self-contained HTML page a person can read or screenshot. AGENTS.md sections collapse by default; pass expanded:true for the full render. Also served at GET /card (?expand=all) over the HTTP transport.",
|
|
165
165
|
inputSchema: {
|
|
166
166
|
type: "object",
|
|
167
167
|
properties: {
|
|
168
168
|
theme: { type: "string", enum: ["light", "dark", "auto"], description: "default: auto" },
|
|
169
169
|
accent: { type: "string", description: "CSS hex colour, e.g. #FF702D (default: the AAIF palette)" },
|
|
170
|
+
expanded: { type: "boolean", description: "render every AGENTS.md section open (default: collapsed)" },
|
|
170
171
|
},
|
|
171
172
|
},
|
|
172
173
|
},
|
|
@@ -215,7 +216,8 @@ export function createServer(root = ROOT) {
|
|
|
215
216
|
}
|
|
216
217
|
case "whoami":
|
|
217
218
|
return text(whoami(root));
|
|
218
|
-
case "render_context_card":
|
|
219
|
+
case "render_context_card": {
|
|
220
|
+
const rawExpanded = args.expanded;
|
|
219
221
|
return {
|
|
220
222
|
content: [
|
|
221
223
|
{
|
|
@@ -223,10 +225,12 @@ export function createServer(root = ROOT) {
|
|
|
223
225
|
text: renderCard(root, {
|
|
224
226
|
theme: (["light", "dark", "auto"].includes(args.theme) ? args.theme : "auto"),
|
|
225
227
|
accent: safeAccent(args.accent),
|
|
228
|
+
expanded: rawExpanded === true || rawExpanded === "true",
|
|
226
229
|
}),
|
|
227
230
|
},
|
|
228
231
|
],
|
|
229
232
|
};
|
|
233
|
+
}
|
|
230
234
|
case "list_context_sources": {
|
|
231
235
|
const doc = parseAgentsMd(AGENTS);
|
|
232
236
|
const mem = parseFafm(FAFM);
|
package/dist/transport/http.js
CHANGED
|
@@ -56,7 +56,7 @@ export function httpApp(root = ROOT) {
|
|
|
56
56
|
const q = c.req.query();
|
|
57
57
|
const theme = (["light", "dark", "auto"].includes(q.theme ?? "") ? q.theme : "auto");
|
|
58
58
|
c.header("content-type", "text/html; charset=utf-8");
|
|
59
|
-
return c.body(renderCard(root, { theme, accent: safeAccent(q.accent) }));
|
|
59
|
+
return c.body(renderCard(root, { theme, accent: safeAccent(q.accent), expanded: q.expand === "all" }));
|
|
60
60
|
});
|
|
61
61
|
// ── Index ───────────────────────────────────────────────────────────
|
|
62
62
|
app.get("/", (c) => c.json({
|
package/docs/MECHANISMS.md
CHANGED
package/docs/card-dark.html
CHANGED
|
@@ -30,7 +30,7 @@ body{margin:0;background:var(--bg);color:var(--fg);
|
|
|
30
30
|
font:15px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
|
|
31
31
|
padding:40px 18px}
|
|
32
32
|
.card{max-width:760px;margin:0 auto;background:var(--card);border:1px solid var(--line);
|
|
33
|
-
border-radius:14px;overflow:
|
|
33
|
+
border-radius:14px;overflow:clip;box-shadow:var(--card-shadow)}
|
|
34
34
|
.card>*{padding:26px 30px}
|
|
35
35
|
.top{border-top:4px solid var(--accent);border-bottom:1px solid var(--line)}
|
|
36
36
|
h1{margin:0 0 10px;font-size:1.7rem;letter-spacing:-.02em}
|
|
@@ -41,9 +41,31 @@ section{border-bottom:1px solid var(--line)}
|
|
|
41
41
|
section:last-child{border-bottom:0}
|
|
42
42
|
.label{font-size:.7rem;font-weight:700;letter-spacing:.16em;text-transform:uppercase;
|
|
43
43
|
color:var(--accent);margin:0 0 14px}
|
|
44
|
-
.toc{display:flex;flex-wrap:wrap;gap:6px 14px;margin:0
|
|
44
|
+
.toc{display:flex;flex-wrap:wrap;gap:6px 14px;margin:0;padding:0;list-style:none}
|
|
45
45
|
.toc a{font-size:.82rem;color:var(--muted);text-decoration:none}
|
|
46
46
|
.toc a:hover{color:var(--accent)}
|
|
47
|
+
/* ── collapsible context ─────────────────────────────────────────── */
|
|
48
|
+
.ctx-nav{position:sticky;top:0;z-index:3;background:var(--card);
|
|
49
|
+
margin:0 -30px 16px;padding:11px 30px;border-bottom:1px solid var(--line);
|
|
50
|
+
display:flex;flex-wrap:wrap;align-items:flex-start;gap:8px 16px}
|
|
51
|
+
.xall{margin-left:auto;flex:none;font:inherit;font-size:.76rem;font-weight:600;
|
|
52
|
+
white-space:nowrap;padding:3px 11px;border-radius:20px;border:1px solid var(--line);
|
|
53
|
+
background:var(--chip);color:var(--muted);cursor:pointer}
|
|
54
|
+
.xall:hover{color:var(--accent);border-color:var(--accent)}
|
|
55
|
+
.ctx-preamble{padding-bottom:4px}
|
|
56
|
+
details.ctx-section{border-top:1px solid var(--line)}
|
|
57
|
+
details.ctx-section>summary{cursor:pointer;list-style:none;padding:11px 0;
|
|
58
|
+
font-weight:600;font-size:1rem;letter-spacing:-.01em;display:flex;gap:9px}
|
|
59
|
+
details.ctx-section>summary::-webkit-details-marker{display:none}
|
|
60
|
+
details.ctx-section>summary::before{content:"›";color:var(--accent);font-weight:700;
|
|
61
|
+
transition:transform .15s ease}
|
|
62
|
+
details.ctx-section[open]>summary::before{transform:rotate(90deg)}
|
|
63
|
+
details.ctx-section>.md{padding:0 0 16px}
|
|
64
|
+
@media print{
|
|
65
|
+
.ctx-nav{display:none}
|
|
66
|
+
details.ctx-section:not([open])>.md{display:block!important}
|
|
67
|
+
details.ctx-section>summary::before{content:""}
|
|
68
|
+
}
|
|
47
69
|
.md h1,.md h2,.md h3,.md h4{margin:22px 0 8px;font-size:1rem;letter-spacing:-.01em}
|
|
48
70
|
.md h1{font-size:1.15rem}
|
|
49
71
|
.md p{margin:8px 0}
|
|
@@ -78,37 +100,21 @@ section:last-child{border-bottom:0}
|
|
|
78
100
|
<main class="card">
|
|
79
101
|
<div class="top">
|
|
80
102
|
<h1>mcp-context-card</h1>
|
|
81
|
-
<div class="pills"><span class="pill">io.github.Wolfe-Jam</span><span class="pill">v1.
|
|
103
|
+
<div class="pills"><span class="pill">io.github.Wolfe-Jam</span><span class="pill">v1.1.0</span><span class="pill accent">published</span><span class="pill">MIT</span></div>
|
|
82
104
|
</div>
|
|
83
105
|
<section>
|
|
84
106
|
<p class="label">Context — AGENTS.md</p>
|
|
85
|
-
<ul class="toc"><li><a href="#setup">Setup</a></li><li><a href="#build">Build</a></li><li><a href="#test">Test</a></li><li><a href="#layout">Layout</a></li><li><a href="#conventions">Conventions</a></li><li><a href="#the-invariant">The invariant</a></li><li><a href="#safety">Safety</a></li><li><a href="#definition-of-done">Definition of done</a></li><li><a href="#authoring-this-file">Authoring this file</a></li></ul><
|
|
86
|
-
<p><code>
|
|
87
|
-
<
|
|
88
|
-
<pre><code class="language-bash">npm ci</code></pre>
|
|
89
|
-
<p>Node 22 or newer. No other system dependencies.</p>
|
|
90
|
-
|
|
91
|
-
<pre><code class="language-bash">npm run build # tsc -p tsconfig.build.json → dist/
|
|
92
|
-
npm run typecheck # tsc --noEmit over src/ + test/</code></pre>
|
|
93
|
-
<h2 id="test">Test</h2>
|
|
94
|
-
<pre><code class="language-bash">npm test # node:test — every test/*.test.ts
|
|
107
|
+
<div class="ctx-nav"><ul class="toc"><li><a href="#setup">Setup</a></li><li><a href="#build">Build</a></li><li><a href="#test">Test</a></li><li><a href="#layout">Layout</a></li><li><a href="#conventions">Conventions</a></li><li><a href="#the-invariant">The invariant</a></li><li><a href="#safety">Safety</a></li><li><a href="#definition-of-done">Definition of done</a></li><li><a href="#authoring-this-file">Authoring this file</a></li></ul><button type="button" class="xall" hidden>Expand all</button></div>
|
|
108
|
+
<div class="ctx-preamble md"><p><code>mcp-context-card</code> is the essential MCP server for a project's <strong>context</strong> (this file), <strong>memory</strong>, and <strong>identity</strong> — usable as your base MCP, or dropped into any existing MCP server as an extension. Discoverable to any MCP client through the two surfaces already in the ecosystem: the Server Card <code>_meta</code> block and <code>ai-catalog.json</code> sibling entries.</p>
|
|
109
|
+
<p><code>read_agents_md</code> serves this file, section by section, over the same MCP connection.</p></div>
|
|
110
|
+
<div class="ctx-body"><details class="ctx-section" id="setup"><summary>Setup</summary><div class="md"><pre><code class="language-bash">npm ci</code></pre>
|
|
111
|
+
<p>Node 22 or newer. No other system dependencies.</p></div></details><details class="ctx-section" id="build"><summary>Build</summary><div class="md"><pre><code class="language-bash">npm run build # tsc -p tsconfig.build.json → dist/
|
|
112
|
+
npm run typecheck # tsc --noEmit over src/ + test/</code></pre></div></details><details class="ctx-section" id="test"><summary>Test</summary><div class="md"><pre><code class="language-bash">npm test # node:test — every test/*.test.ts
|
|
95
113
|
npm run test:coverage # + the coverage gate (lines 90 / funcs 85 / branches 80, src/ only)
|
|
96
114
|
npm run demo # end to end: all tools over stdio, then over stateless HTTP
|
|
97
115
|
npm run version:check # every version-bearing spot agrees with package.json
|
|
98
116
|
npm run faf:check # project.faf / project.fafm / .well-known/fafa still describe the project</code></pre>
|
|
99
|
-
<p>CI runs <code>version:check → faf:check → typecheck → build → test:coverage → demo</code> on Linux, macOS, and Windows for every push and PR to <code>main</code> (<code>.github/workflows/ci.yml</code>), plus <code>catalog:check</code> / <code>card:check</code>, <code>faf-cli check project.faf --strict</code> (the repo dogfoods a <code>project.faf</code> — this keeps it Trophy), and <code>faf:nudge</code> (PR-only, non-blocking — warns if the code's shape moved without <code>project.faf</code>) on Linux.</p>
|
|
100
|
-
<h2 id="layout">Layout</h2>
|
|
101
|
-
<table><thead><tr><th>Path</th><th>What</th></tr></thead><tbody><tr><td><code>src/server.ts</code></td><td>the MCP server — the nine tools + the Server Card resource</td></tr><tr><td><code>src/agents-md.ts</code></td><td>reads and section-splits this file</td></tr><tr><td><code>src/author.ts</code></td><td><code>author_agents_md</code> — BETTER via <code>agents-md-facts</code>, BEST when <code>project.faf</code> exists</td></tr><tr><td><code>src/md.ts</code></td><td>a minimal dependency-free Markdown → HTML renderer</td></tr><tr><td><code>src/render-card.ts</code></td><td>the card — identity + this file + memory + discovery, as one HTML page</td></tr><tr><td><code>src/memory.ts</code> → <code>src/faf/parse-fafm.ts</code></td><td>file-backed <code>remember</code> / <code>recall</code> / <code>forget</code></td></tr><tr><td><code>src/identity.ts</code></td><td><code>whoami</code> (<code>.fafa</code> → <code>package.json</code> fallback) + the <code>_meta</code> context block</td></tr><tr><td><code>src/catalog-gen.ts</code></td><td>writes <code>.well-known/ai-catalog.json</code> from the same three sources</td></tr><tr><td><code>src/transport/http.ts</code></td><td>the stateless Streamable HTTP app (Hono)</td></tr><tr><td><code>src/bin.ts</code></td><td>the entry point (<code>resolveLaunch</code>) — <code>stdio</code> · <code>--http</code> · <code>card</code> · <code>--help</code> · <code>--version</code></td></tr><tr><td><code>src/faf/parse-fafm.ts</code> · <code>parse-fafa.ts</code></td><td>the <code>.fafm</code> / <code>.fafa</code> parsers</td></tr></tbody></table>
|
|
102
|
-
<h2 id="conventions">Conventions</h2>
|
|
103
|
-
<ul><li>TypeScript strict, ESM only (<code>"type": "module"</code>, <code>.js</code> import specifiers).</li><li>Tests use <code>node:test</code> + <code>node:assert/strict</code> — no test framework.</li><li>Every source file opens with a comment stating what it is and why.</li><li>Conventional Commit messages (<code>feat:</code>, <code>fix:</code>, <code>chore:</code>, <code>test:</code>, <code>docs:</code>).</li></ul>
|
|
104
|
-
<h2 id="the-invariant">The invariant</h2>
|
|
105
|
-
<p><code>src/identity.ts</code>, <code>src/catalog-gen.ts</code>, and <code>src/render-card.ts</code> all describe the <strong>same three sources</strong>: this file, <code>project.fafm</code>, <code>.well-known/fafa</code>. Change what one exposes and you must change the others. <code>npm run catalog:check</code> and <code>npm run card:check</code> enforce it in CI — each regenerates its surface and fails on any diff.</p>
|
|
106
|
-
<h2 id="safety">Safety</h2>
|
|
107
|
-
<ul><li>Branch off <code>main</code>; CI must be green before merge.</li><li><code>npm run demo</code> writes a fact to <code>project.fafm</code> and restores the file on exit — don't kill it mid-run.</li><li>No secrets live in this repo; never add any.</li></ul>
|
|
108
|
-
<h2 id="definition-of-done">Definition of done</h2>
|
|
109
|
-
<p><code>npm run typecheck && npm run build && npm test && npm run demo</code> all green, plus <code>npm run catalog:check</code> and <code>npm run card:check</code> clean if you touched <code>AGENTS.md</code>, <code>project.fafm</code>, or <code>.well-known/fafa</code>, and <code>npm run faf:check</code> green if you changed the layout, a dependency, or the identity. On a version bump, <code>npm run version:check</code> green (it lists every spot that must move together) and <code>project.faf</code> still Trophy (<code>faf-cli check project.faf --strict</code>). If CI's <code>faf:nudge</code> warns on your PR, reconcile <code>project.faf</code> (and re-check <code>project.fafm</code> facts if <code>AGENTS.md</code> moved) or say why it's fine.</p>
|
|
110
|
-
<h2 id="authoring-this-file">Authoring this file</h2>
|
|
111
|
-
<p><code>AGENTS.md</code> here is maintained by hand. The <code>author_agents_md</code> tool (or <code>faf export --agents</code>) would draft a BEST version straight from this repo's own <code>project.faf</code> plus its detected facts — the server doesn't care how the file was authored, only that it's valid Markdown.</p></div>
|
|
117
|
+
<p>CI runs <code>version:check → faf:check → typecheck → build → test:coverage → demo</code> on Linux, macOS, and Windows for every push and PR to <code>main</code> (<code>.github/workflows/ci.yml</code>), plus <code>catalog:check</code> / <code>card:check</code>, <code>faf-cli check project.faf --strict</code> (the repo dogfoods a <code>project.faf</code> — this keeps it Trophy), and <code>faf:nudge</code> (PR-only, non-blocking — warns if the code's shape moved without <code>project.faf</code>) on Linux.</p></div></details><details class="ctx-section" id="layout"><summary>Layout</summary><div class="md"><table><thead><tr><th>Path</th><th>What</th></tr></thead><tbody><tr><td><code>src/server.ts</code></td><td>the MCP server — the nine tools + the Server Card resource</td></tr><tr><td><code>src/agents-md.ts</code></td><td>reads and section-splits this file</td></tr><tr><td><code>src/author.ts</code></td><td><code>author_agents_md</code> — BETTER via <code>agents-md-facts</code>, BEST when <code>project.faf</code> exists</td></tr><tr><td><code>src/md.ts</code></td><td>a minimal dependency-free Markdown → HTML renderer</td></tr><tr><td><code>src/render-card.ts</code></td><td>the card — identity + this file + memory + discovery, as one HTML page</td></tr><tr><td><code>src/memory.ts</code> → <code>src/faf/parse-fafm.ts</code></td><td>file-backed <code>remember</code> / <code>recall</code> / <code>forget</code></td></tr><tr><td><code>src/identity.ts</code></td><td><code>whoami</code> (<code>.fafa</code> → <code>package.json</code> fallback) + the <code>_meta</code> context block</td></tr><tr><td><code>src/catalog-gen.ts</code></td><td>writes <code>.well-known/ai-catalog.json</code> from the same three sources</td></tr><tr><td><code>src/transport/http.ts</code></td><td>the stateless Streamable HTTP app (Hono)</td></tr><tr><td><code>src/bin.ts</code></td><td>the entry point (<code>resolveLaunch</code>) — <code>stdio</code> · <code>--http</code> · <code>card</code> · <code>--help</code> · <code>--version</code></td></tr><tr><td><code>src/faf/parse-fafm.ts</code> · <code>parse-fafa.ts</code></td><td>the <code>.fafm</code> / <code>.fafa</code> parsers</td></tr></tbody></table></div></details><details class="ctx-section" id="conventions"><summary>Conventions</summary><div class="md"><ul><li>TypeScript strict, ESM only (<code>"type": "module"</code>, <code>.js</code> import specifiers).</li><li>Tests use <code>node:test</code> + <code>node:assert/strict</code> — no test framework.</li><li>Every source file opens with a comment stating what it is and why.</li><li>Conventional Commit messages (<code>feat:</code>, <code>fix:</code>, <code>chore:</code>, <code>test:</code>, <code>docs:</code>).</li></ul></div></details><details class="ctx-section" id="the-invariant"><summary>The invariant</summary><div class="md"><p><code>src/identity.ts</code>, <code>src/catalog-gen.ts</code>, and <code>src/render-card.ts</code> all describe the <strong>same three sources</strong>: this file, <code>project.fafm</code>, <code>.well-known/fafa</code>. Change what one exposes and you must change the others. <code>npm run catalog:check</code> and <code>npm run card:check</code> enforce it in CI — each regenerates its surface and fails on any diff.</p></div></details><details class="ctx-section" id="safety"><summary>Safety</summary><div class="md"><ul><li>Branch off <code>main</code>; CI must be green before merge.</li><li><code>npm run demo</code> writes a fact to <code>project.fafm</code> and restores the file on exit — don't kill it mid-run.</li><li>No secrets live in this repo; never add any.</li></ul></div></details><details class="ctx-section" id="definition-of-done"><summary>Definition of done</summary><div class="md"><p><code>npm run typecheck && npm run build && npm test && npm run demo</code> all green, plus <code>npm run catalog:check</code> and <code>npm run card:check</code> clean if you touched <code>AGENTS.md</code>, <code>project.fafm</code>, or <code>.well-known/fafa</code>, and <code>npm run faf:check</code> green if you changed the layout, a dependency, or the identity. On a version bump, <code>npm run version:check</code> green (it lists every spot that must move together) and <code>project.faf</code> still Trophy (<code>faf-cli check project.faf --strict</code>). If CI's <code>faf:nudge</code> warns on your PR, reconcile <code>project.faf</code> (and re-check <code>project.fafm</code> facts if <code>AGENTS.md</code> moved) or say why it's fine.</p></div></details><details class="ctx-section" id="authoring-this-file"><summary>Authoring this file</summary><div class="md"><p><code>AGENTS.md</code> here is maintained by hand. The <code>author_agents_md</code> tool (or <code>faf export --agents</code>) would draft a BEST version straight from this repo's own <code>project.faf</code> plus its detected facts — the server doesn't care how the file was authored, only that it's valid Markdown.</p></div></details></div>
|
|
112
118
|
</section>
|
|
113
119
|
<section>
|
|
114
120
|
<p class="label">Memory — 4 facts</p>
|
|
@@ -124,5 +130,25 @@ npm run faf:check # project.faf / project.fafm / .well-known/fafa still desc
|
|
|
124
130
|
</section>
|
|
125
131
|
<div class="foot">mcp-context-card · context card</div>
|
|
126
132
|
</main>
|
|
133
|
+
<script>
|
|
134
|
+
(function(){
|
|
135
|
+
var btn=document.querySelector(".xall");
|
|
136
|
+
var secs=[].slice.call(document.querySelectorAll("details.ctx-section"));
|
|
137
|
+
if(!btn||!secs.length)return;
|
|
138
|
+
var sync=function(){btn.textContent=secs.every(function(d){return d.open})?"Collapse all":"Expand all"};
|
|
139
|
+
btn.hidden=false;
|
|
140
|
+
btn.addEventListener("click",function(){
|
|
141
|
+
var open=!secs.every(function(d){return d.open});
|
|
142
|
+
secs.forEach(function(d){d.open=open});sync();
|
|
143
|
+
});
|
|
144
|
+
secs.forEach(function(d){d.addEventListener("toggle",sync)});
|
|
145
|
+
var openHash=function(){var d=document.getElementById(location.hash.slice(1));if(d&&d.tagName==="DETAILS")d.open=true};
|
|
146
|
+
addEventListener("hashchange",openHash);openHash();
|
|
147
|
+
var pre=[];
|
|
148
|
+
addEventListener("beforeprint",function(){pre=secs.map(function(d){return d.open});secs.forEach(function(d){d.open=true})});
|
|
149
|
+
addEventListener("afterprint",function(){secs.forEach(function(d,i){d.open=pre[i]});sync()});
|
|
150
|
+
sync();
|
|
151
|
+
})();
|
|
152
|
+
</script>
|
|
127
153
|
</body>
|
|
128
154
|
</html>
|
package/docs/card-light.html
CHANGED
|
@@ -30,7 +30,7 @@ body{margin:0;background:var(--bg);color:var(--fg);
|
|
|
30
30
|
font:15px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
|
|
31
31
|
padding:40px 18px}
|
|
32
32
|
.card{max-width:760px;margin:0 auto;background:var(--card);border:1px solid var(--line);
|
|
33
|
-
border-radius:14px;overflow:
|
|
33
|
+
border-radius:14px;overflow:clip;box-shadow:var(--card-shadow)}
|
|
34
34
|
.card>*{padding:26px 30px}
|
|
35
35
|
.top{border-top:4px solid var(--accent);border-bottom:1px solid var(--line)}
|
|
36
36
|
h1{margin:0 0 10px;font-size:1.7rem;letter-spacing:-.02em}
|
|
@@ -41,9 +41,31 @@ section{border-bottom:1px solid var(--line)}
|
|
|
41
41
|
section:last-child{border-bottom:0}
|
|
42
42
|
.label{font-size:.7rem;font-weight:700;letter-spacing:.16em;text-transform:uppercase;
|
|
43
43
|
color:var(--accent);margin:0 0 14px}
|
|
44
|
-
.toc{display:flex;flex-wrap:wrap;gap:6px 14px;margin:0
|
|
44
|
+
.toc{display:flex;flex-wrap:wrap;gap:6px 14px;margin:0;padding:0;list-style:none}
|
|
45
45
|
.toc a{font-size:.82rem;color:var(--muted);text-decoration:none}
|
|
46
46
|
.toc a:hover{color:var(--accent)}
|
|
47
|
+
/* ── collapsible context ─────────────────────────────────────────── */
|
|
48
|
+
.ctx-nav{position:sticky;top:0;z-index:3;background:var(--card);
|
|
49
|
+
margin:0 -30px 16px;padding:11px 30px;border-bottom:1px solid var(--line);
|
|
50
|
+
display:flex;flex-wrap:wrap;align-items:flex-start;gap:8px 16px}
|
|
51
|
+
.xall{margin-left:auto;flex:none;font:inherit;font-size:.76rem;font-weight:600;
|
|
52
|
+
white-space:nowrap;padding:3px 11px;border-radius:20px;border:1px solid var(--line);
|
|
53
|
+
background:var(--chip);color:var(--muted);cursor:pointer}
|
|
54
|
+
.xall:hover{color:var(--accent);border-color:var(--accent)}
|
|
55
|
+
.ctx-preamble{padding-bottom:4px}
|
|
56
|
+
details.ctx-section{border-top:1px solid var(--line)}
|
|
57
|
+
details.ctx-section>summary{cursor:pointer;list-style:none;padding:11px 0;
|
|
58
|
+
font-weight:600;font-size:1rem;letter-spacing:-.01em;display:flex;gap:9px}
|
|
59
|
+
details.ctx-section>summary::-webkit-details-marker{display:none}
|
|
60
|
+
details.ctx-section>summary::before{content:"›";color:var(--accent);font-weight:700;
|
|
61
|
+
transition:transform .15s ease}
|
|
62
|
+
details.ctx-section[open]>summary::before{transform:rotate(90deg)}
|
|
63
|
+
details.ctx-section>.md{padding:0 0 16px}
|
|
64
|
+
@media print{
|
|
65
|
+
.ctx-nav{display:none}
|
|
66
|
+
details.ctx-section:not([open])>.md{display:block!important}
|
|
67
|
+
details.ctx-section>summary::before{content:""}
|
|
68
|
+
}
|
|
47
69
|
.md h1,.md h2,.md h3,.md h4{margin:22px 0 8px;font-size:1rem;letter-spacing:-.01em}
|
|
48
70
|
.md h1{font-size:1.15rem}
|
|
49
71
|
.md p{margin:8px 0}
|
|
@@ -78,37 +100,21 @@ section:last-child{border-bottom:0}
|
|
|
78
100
|
<main class="card">
|
|
79
101
|
<div class="top">
|
|
80
102
|
<h1>mcp-context-card</h1>
|
|
81
|
-
<div class="pills"><span class="pill">io.github.Wolfe-Jam</span><span class="pill">v1.
|
|
103
|
+
<div class="pills"><span class="pill">io.github.Wolfe-Jam</span><span class="pill">v1.1.0</span><span class="pill accent">published</span><span class="pill">MIT</span></div>
|
|
82
104
|
</div>
|
|
83
105
|
<section>
|
|
84
106
|
<p class="label">Context — AGENTS.md</p>
|
|
85
|
-
<ul class="toc"><li><a href="#setup">Setup</a></li><li><a href="#build">Build</a></li><li><a href="#test">Test</a></li><li><a href="#layout">Layout</a></li><li><a href="#conventions">Conventions</a></li><li><a href="#the-invariant">The invariant</a></li><li><a href="#safety">Safety</a></li><li><a href="#definition-of-done">Definition of done</a></li><li><a href="#authoring-this-file">Authoring this file</a></li></ul><
|
|
86
|
-
<p><code>
|
|
87
|
-
<
|
|
88
|
-
<pre><code class="language-bash">npm ci</code></pre>
|
|
89
|
-
<p>Node 22 or newer. No other system dependencies.</p>
|
|
90
|
-
|
|
91
|
-
<pre><code class="language-bash">npm run build # tsc -p tsconfig.build.json → dist/
|
|
92
|
-
npm run typecheck # tsc --noEmit over src/ + test/</code></pre>
|
|
93
|
-
<h2 id="test">Test</h2>
|
|
94
|
-
<pre><code class="language-bash">npm test # node:test — every test/*.test.ts
|
|
107
|
+
<div class="ctx-nav"><ul class="toc"><li><a href="#setup">Setup</a></li><li><a href="#build">Build</a></li><li><a href="#test">Test</a></li><li><a href="#layout">Layout</a></li><li><a href="#conventions">Conventions</a></li><li><a href="#the-invariant">The invariant</a></li><li><a href="#safety">Safety</a></li><li><a href="#definition-of-done">Definition of done</a></li><li><a href="#authoring-this-file">Authoring this file</a></li></ul><button type="button" class="xall" hidden>Expand all</button></div>
|
|
108
|
+
<div class="ctx-preamble md"><p><code>mcp-context-card</code> is the essential MCP server for a project's <strong>context</strong> (this file), <strong>memory</strong>, and <strong>identity</strong> — usable as your base MCP, or dropped into any existing MCP server as an extension. Discoverable to any MCP client through the two surfaces already in the ecosystem: the Server Card <code>_meta</code> block and <code>ai-catalog.json</code> sibling entries.</p>
|
|
109
|
+
<p><code>read_agents_md</code> serves this file, section by section, over the same MCP connection.</p></div>
|
|
110
|
+
<div class="ctx-body"><details class="ctx-section" id="setup"><summary>Setup</summary><div class="md"><pre><code class="language-bash">npm ci</code></pre>
|
|
111
|
+
<p>Node 22 or newer. No other system dependencies.</p></div></details><details class="ctx-section" id="build"><summary>Build</summary><div class="md"><pre><code class="language-bash">npm run build # tsc -p tsconfig.build.json → dist/
|
|
112
|
+
npm run typecheck # tsc --noEmit over src/ + test/</code></pre></div></details><details class="ctx-section" id="test"><summary>Test</summary><div class="md"><pre><code class="language-bash">npm test # node:test — every test/*.test.ts
|
|
95
113
|
npm run test:coverage # + the coverage gate (lines 90 / funcs 85 / branches 80, src/ only)
|
|
96
114
|
npm run demo # end to end: all tools over stdio, then over stateless HTTP
|
|
97
115
|
npm run version:check # every version-bearing spot agrees with package.json
|
|
98
116
|
npm run faf:check # project.faf / project.fafm / .well-known/fafa still describe the project</code></pre>
|
|
99
|
-
<p>CI runs <code>version:check → faf:check → typecheck → build → test:coverage → demo</code> on Linux, macOS, and Windows for every push and PR to <code>main</code> (<code>.github/workflows/ci.yml</code>), plus <code>catalog:check</code> / <code>card:check</code>, <code>faf-cli check project.faf --strict</code> (the repo dogfoods a <code>project.faf</code> — this keeps it Trophy), and <code>faf:nudge</code> (PR-only, non-blocking — warns if the code's shape moved without <code>project.faf</code>) on Linux.</p>
|
|
100
|
-
<h2 id="layout">Layout</h2>
|
|
101
|
-
<table><thead><tr><th>Path</th><th>What</th></tr></thead><tbody><tr><td><code>src/server.ts</code></td><td>the MCP server — the nine tools + the Server Card resource</td></tr><tr><td><code>src/agents-md.ts</code></td><td>reads and section-splits this file</td></tr><tr><td><code>src/author.ts</code></td><td><code>author_agents_md</code> — BETTER via <code>agents-md-facts</code>, BEST when <code>project.faf</code> exists</td></tr><tr><td><code>src/md.ts</code></td><td>a minimal dependency-free Markdown → HTML renderer</td></tr><tr><td><code>src/render-card.ts</code></td><td>the card — identity + this file + memory + discovery, as one HTML page</td></tr><tr><td><code>src/memory.ts</code> → <code>src/faf/parse-fafm.ts</code></td><td>file-backed <code>remember</code> / <code>recall</code> / <code>forget</code></td></tr><tr><td><code>src/identity.ts</code></td><td><code>whoami</code> (<code>.fafa</code> → <code>package.json</code> fallback) + the <code>_meta</code> context block</td></tr><tr><td><code>src/catalog-gen.ts</code></td><td>writes <code>.well-known/ai-catalog.json</code> from the same three sources</td></tr><tr><td><code>src/transport/http.ts</code></td><td>the stateless Streamable HTTP app (Hono)</td></tr><tr><td><code>src/bin.ts</code></td><td>the entry point (<code>resolveLaunch</code>) — <code>stdio</code> · <code>--http</code> · <code>card</code> · <code>--help</code> · <code>--version</code></td></tr><tr><td><code>src/faf/parse-fafm.ts</code> · <code>parse-fafa.ts</code></td><td>the <code>.fafm</code> / <code>.fafa</code> parsers</td></tr></tbody></table>
|
|
102
|
-
<h2 id="conventions">Conventions</h2>
|
|
103
|
-
<ul><li>TypeScript strict, ESM only (<code>"type": "module"</code>, <code>.js</code> import specifiers).</li><li>Tests use <code>node:test</code> + <code>node:assert/strict</code> — no test framework.</li><li>Every source file opens with a comment stating what it is and why.</li><li>Conventional Commit messages (<code>feat:</code>, <code>fix:</code>, <code>chore:</code>, <code>test:</code>, <code>docs:</code>).</li></ul>
|
|
104
|
-
<h2 id="the-invariant">The invariant</h2>
|
|
105
|
-
<p><code>src/identity.ts</code>, <code>src/catalog-gen.ts</code>, and <code>src/render-card.ts</code> all describe the <strong>same three sources</strong>: this file, <code>project.fafm</code>, <code>.well-known/fafa</code>. Change what one exposes and you must change the others. <code>npm run catalog:check</code> and <code>npm run card:check</code> enforce it in CI — each regenerates its surface and fails on any diff.</p>
|
|
106
|
-
<h2 id="safety">Safety</h2>
|
|
107
|
-
<ul><li>Branch off <code>main</code>; CI must be green before merge.</li><li><code>npm run demo</code> writes a fact to <code>project.fafm</code> and restores the file on exit — don't kill it mid-run.</li><li>No secrets live in this repo; never add any.</li></ul>
|
|
108
|
-
<h2 id="definition-of-done">Definition of done</h2>
|
|
109
|
-
<p><code>npm run typecheck && npm run build && npm test && npm run demo</code> all green, plus <code>npm run catalog:check</code> and <code>npm run card:check</code> clean if you touched <code>AGENTS.md</code>, <code>project.fafm</code>, or <code>.well-known/fafa</code>, and <code>npm run faf:check</code> green if you changed the layout, a dependency, or the identity. On a version bump, <code>npm run version:check</code> green (it lists every spot that must move together) and <code>project.faf</code> still Trophy (<code>faf-cli check project.faf --strict</code>). If CI's <code>faf:nudge</code> warns on your PR, reconcile <code>project.faf</code> (and re-check <code>project.fafm</code> facts if <code>AGENTS.md</code> moved) or say why it's fine.</p>
|
|
110
|
-
<h2 id="authoring-this-file">Authoring this file</h2>
|
|
111
|
-
<p><code>AGENTS.md</code> here is maintained by hand. The <code>author_agents_md</code> tool (or <code>faf export --agents</code>) would draft a BEST version straight from this repo's own <code>project.faf</code> plus its detected facts — the server doesn't care how the file was authored, only that it's valid Markdown.</p></div>
|
|
117
|
+
<p>CI runs <code>version:check → faf:check → typecheck → build → test:coverage → demo</code> on Linux, macOS, and Windows for every push and PR to <code>main</code> (<code>.github/workflows/ci.yml</code>), plus <code>catalog:check</code> / <code>card:check</code>, <code>faf-cli check project.faf --strict</code> (the repo dogfoods a <code>project.faf</code> — this keeps it Trophy), and <code>faf:nudge</code> (PR-only, non-blocking — warns if the code's shape moved without <code>project.faf</code>) on Linux.</p></div></details><details class="ctx-section" id="layout"><summary>Layout</summary><div class="md"><table><thead><tr><th>Path</th><th>What</th></tr></thead><tbody><tr><td><code>src/server.ts</code></td><td>the MCP server — the nine tools + the Server Card resource</td></tr><tr><td><code>src/agents-md.ts</code></td><td>reads and section-splits this file</td></tr><tr><td><code>src/author.ts</code></td><td><code>author_agents_md</code> — BETTER via <code>agents-md-facts</code>, BEST when <code>project.faf</code> exists</td></tr><tr><td><code>src/md.ts</code></td><td>a minimal dependency-free Markdown → HTML renderer</td></tr><tr><td><code>src/render-card.ts</code></td><td>the card — identity + this file + memory + discovery, as one HTML page</td></tr><tr><td><code>src/memory.ts</code> → <code>src/faf/parse-fafm.ts</code></td><td>file-backed <code>remember</code> / <code>recall</code> / <code>forget</code></td></tr><tr><td><code>src/identity.ts</code></td><td><code>whoami</code> (<code>.fafa</code> → <code>package.json</code> fallback) + the <code>_meta</code> context block</td></tr><tr><td><code>src/catalog-gen.ts</code></td><td>writes <code>.well-known/ai-catalog.json</code> from the same three sources</td></tr><tr><td><code>src/transport/http.ts</code></td><td>the stateless Streamable HTTP app (Hono)</td></tr><tr><td><code>src/bin.ts</code></td><td>the entry point (<code>resolveLaunch</code>) — <code>stdio</code> · <code>--http</code> · <code>card</code> · <code>--help</code> · <code>--version</code></td></tr><tr><td><code>src/faf/parse-fafm.ts</code> · <code>parse-fafa.ts</code></td><td>the <code>.fafm</code> / <code>.fafa</code> parsers</td></tr></tbody></table></div></details><details class="ctx-section" id="conventions"><summary>Conventions</summary><div class="md"><ul><li>TypeScript strict, ESM only (<code>"type": "module"</code>, <code>.js</code> import specifiers).</li><li>Tests use <code>node:test</code> + <code>node:assert/strict</code> — no test framework.</li><li>Every source file opens with a comment stating what it is and why.</li><li>Conventional Commit messages (<code>feat:</code>, <code>fix:</code>, <code>chore:</code>, <code>test:</code>, <code>docs:</code>).</li></ul></div></details><details class="ctx-section" id="the-invariant"><summary>The invariant</summary><div class="md"><p><code>src/identity.ts</code>, <code>src/catalog-gen.ts</code>, and <code>src/render-card.ts</code> all describe the <strong>same three sources</strong>: this file, <code>project.fafm</code>, <code>.well-known/fafa</code>. Change what one exposes and you must change the others. <code>npm run catalog:check</code> and <code>npm run card:check</code> enforce it in CI — each regenerates its surface and fails on any diff.</p></div></details><details class="ctx-section" id="safety"><summary>Safety</summary><div class="md"><ul><li>Branch off <code>main</code>; CI must be green before merge.</li><li><code>npm run demo</code> writes a fact to <code>project.fafm</code> and restores the file on exit — don't kill it mid-run.</li><li>No secrets live in this repo; never add any.</li></ul></div></details><details class="ctx-section" id="definition-of-done"><summary>Definition of done</summary><div class="md"><p><code>npm run typecheck && npm run build && npm test && npm run demo</code> all green, plus <code>npm run catalog:check</code> and <code>npm run card:check</code> clean if you touched <code>AGENTS.md</code>, <code>project.fafm</code>, or <code>.well-known/fafa</code>, and <code>npm run faf:check</code> green if you changed the layout, a dependency, or the identity. On a version bump, <code>npm run version:check</code> green (it lists every spot that must move together) and <code>project.faf</code> still Trophy (<code>faf-cli check project.faf --strict</code>). If CI's <code>faf:nudge</code> warns on your PR, reconcile <code>project.faf</code> (and re-check <code>project.fafm</code> facts if <code>AGENTS.md</code> moved) or say why it's fine.</p></div></details><details class="ctx-section" id="authoring-this-file"><summary>Authoring this file</summary><div class="md"><p><code>AGENTS.md</code> here is maintained by hand. The <code>author_agents_md</code> tool (or <code>faf export --agents</code>) would draft a BEST version straight from this repo's own <code>project.faf</code> plus its detected facts — the server doesn't care how the file was authored, only that it's valid Markdown.</p></div></details></div>
|
|
112
118
|
</section>
|
|
113
119
|
<section>
|
|
114
120
|
<p class="label">Memory — 4 facts</p>
|
|
@@ -124,5 +130,25 @@ npm run faf:check # project.faf / project.fafm / .well-known/fafa still desc
|
|
|
124
130
|
</section>
|
|
125
131
|
<div class="foot">mcp-context-card · context card</div>
|
|
126
132
|
</main>
|
|
133
|
+
<script>
|
|
134
|
+
(function(){
|
|
135
|
+
var btn=document.querySelector(".xall");
|
|
136
|
+
var secs=[].slice.call(document.querySelectorAll("details.ctx-section"));
|
|
137
|
+
if(!btn||!secs.length)return;
|
|
138
|
+
var sync=function(){btn.textContent=secs.every(function(d){return d.open})?"Collapse all":"Expand all"};
|
|
139
|
+
btn.hidden=false;
|
|
140
|
+
btn.addEventListener("click",function(){
|
|
141
|
+
var open=!secs.every(function(d){return d.open});
|
|
142
|
+
secs.forEach(function(d){d.open=open});sync();
|
|
143
|
+
});
|
|
144
|
+
secs.forEach(function(d){d.addEventListener("toggle",sync)});
|
|
145
|
+
var openHash=function(){var d=document.getElementById(location.hash.slice(1));if(d&&d.tagName==="DETAILS")d.open=true};
|
|
146
|
+
addEventListener("hashchange",openHash);openHash();
|
|
147
|
+
var pre=[];
|
|
148
|
+
addEventListener("beforeprint",function(){pre=secs.map(function(d){return d.open});secs.forEach(function(d){d.open=true})});
|
|
149
|
+
addEventListener("afterprint",function(){secs.forEach(function(d,i){d.open=pre[i]});sync()});
|
|
150
|
+
sync();
|
|
151
|
+
})();
|
|
152
|
+
</script>
|
|
127
153
|
</body>
|
|
128
154
|
</html>
|
package/docs/card.html
CHANGED
|
@@ -30,7 +30,7 @@ body{margin:0;background:var(--bg);color:var(--fg);
|
|
|
30
30
|
font:15px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
|
|
31
31
|
padding:40px 18px}
|
|
32
32
|
.card{max-width:760px;margin:0 auto;background:var(--card);border:1px solid var(--line);
|
|
33
|
-
border-radius:14px;overflow:
|
|
33
|
+
border-radius:14px;overflow:clip;box-shadow:var(--card-shadow)}
|
|
34
34
|
.card>*{padding:26px 30px}
|
|
35
35
|
.top{border-top:4px solid var(--accent);border-bottom:1px solid var(--line)}
|
|
36
36
|
h1{margin:0 0 10px;font-size:1.7rem;letter-spacing:-.02em}
|
|
@@ -41,9 +41,31 @@ section{border-bottom:1px solid var(--line)}
|
|
|
41
41
|
section:last-child{border-bottom:0}
|
|
42
42
|
.label{font-size:.7rem;font-weight:700;letter-spacing:.16em;text-transform:uppercase;
|
|
43
43
|
color:var(--accent);margin:0 0 14px}
|
|
44
|
-
.toc{display:flex;flex-wrap:wrap;gap:6px 14px;margin:0
|
|
44
|
+
.toc{display:flex;flex-wrap:wrap;gap:6px 14px;margin:0;padding:0;list-style:none}
|
|
45
45
|
.toc a{font-size:.82rem;color:var(--muted);text-decoration:none}
|
|
46
46
|
.toc a:hover{color:var(--accent)}
|
|
47
|
+
/* ── collapsible context ─────────────────────────────────────────── */
|
|
48
|
+
.ctx-nav{position:sticky;top:0;z-index:3;background:var(--card);
|
|
49
|
+
margin:0 -30px 16px;padding:11px 30px;border-bottom:1px solid var(--line);
|
|
50
|
+
display:flex;flex-wrap:wrap;align-items:flex-start;gap:8px 16px}
|
|
51
|
+
.xall{margin-left:auto;flex:none;font:inherit;font-size:.76rem;font-weight:600;
|
|
52
|
+
white-space:nowrap;padding:3px 11px;border-radius:20px;border:1px solid var(--line);
|
|
53
|
+
background:var(--chip);color:var(--muted);cursor:pointer}
|
|
54
|
+
.xall:hover{color:var(--accent);border-color:var(--accent)}
|
|
55
|
+
.ctx-preamble{padding-bottom:4px}
|
|
56
|
+
details.ctx-section{border-top:1px solid var(--line)}
|
|
57
|
+
details.ctx-section>summary{cursor:pointer;list-style:none;padding:11px 0;
|
|
58
|
+
font-weight:600;font-size:1rem;letter-spacing:-.01em;display:flex;gap:9px}
|
|
59
|
+
details.ctx-section>summary::-webkit-details-marker{display:none}
|
|
60
|
+
details.ctx-section>summary::before{content:"›";color:var(--accent);font-weight:700;
|
|
61
|
+
transition:transform .15s ease}
|
|
62
|
+
details.ctx-section[open]>summary::before{transform:rotate(90deg)}
|
|
63
|
+
details.ctx-section>.md{padding:0 0 16px}
|
|
64
|
+
@media print{
|
|
65
|
+
.ctx-nav{display:none}
|
|
66
|
+
details.ctx-section:not([open])>.md{display:block!important}
|
|
67
|
+
details.ctx-section>summary::before{content:""}
|
|
68
|
+
}
|
|
47
69
|
.md h1,.md h2,.md h3,.md h4{margin:22px 0 8px;font-size:1rem;letter-spacing:-.01em}
|
|
48
70
|
.md h1{font-size:1.15rem}
|
|
49
71
|
.md p{margin:8px 0}
|
|
@@ -78,37 +100,21 @@ section:last-child{border-bottom:0}
|
|
|
78
100
|
<main class="card">
|
|
79
101
|
<div class="top">
|
|
80
102
|
<h1>mcp-context-card</h1>
|
|
81
|
-
<div class="pills"><span class="pill">io.github.Wolfe-Jam</span><span class="pill">v1.
|
|
103
|
+
<div class="pills"><span class="pill">io.github.Wolfe-Jam</span><span class="pill">v1.1.0</span><span class="pill accent">published</span><span class="pill">MIT</span></div>
|
|
82
104
|
</div>
|
|
83
105
|
<section>
|
|
84
106
|
<p class="label">Context — AGENTS.md</p>
|
|
85
|
-
<ul class="toc"><li><a href="#setup">Setup</a></li><li><a href="#build">Build</a></li><li><a href="#test">Test</a></li><li><a href="#layout">Layout</a></li><li><a href="#conventions">Conventions</a></li><li><a href="#the-invariant">The invariant</a></li><li><a href="#safety">Safety</a></li><li><a href="#definition-of-done">Definition of done</a></li><li><a href="#authoring-this-file">Authoring this file</a></li></ul><
|
|
86
|
-
<p><code>
|
|
87
|
-
<
|
|
88
|
-
<pre><code class="language-bash">npm ci</code></pre>
|
|
89
|
-
<p>Node 22 or newer. No other system dependencies.</p>
|
|
90
|
-
|
|
91
|
-
<pre><code class="language-bash">npm run build # tsc -p tsconfig.build.json → dist/
|
|
92
|
-
npm run typecheck # tsc --noEmit over src/ + test/</code></pre>
|
|
93
|
-
<h2 id="test">Test</h2>
|
|
94
|
-
<pre><code class="language-bash">npm test # node:test — every test/*.test.ts
|
|
107
|
+
<div class="ctx-nav"><ul class="toc"><li><a href="#setup">Setup</a></li><li><a href="#build">Build</a></li><li><a href="#test">Test</a></li><li><a href="#layout">Layout</a></li><li><a href="#conventions">Conventions</a></li><li><a href="#the-invariant">The invariant</a></li><li><a href="#safety">Safety</a></li><li><a href="#definition-of-done">Definition of done</a></li><li><a href="#authoring-this-file">Authoring this file</a></li></ul><button type="button" class="xall" hidden>Expand all</button></div>
|
|
108
|
+
<div class="ctx-preamble md"><p><code>mcp-context-card</code> is the essential MCP server for a project's <strong>context</strong> (this file), <strong>memory</strong>, and <strong>identity</strong> — usable as your base MCP, or dropped into any existing MCP server as an extension. Discoverable to any MCP client through the two surfaces already in the ecosystem: the Server Card <code>_meta</code> block and <code>ai-catalog.json</code> sibling entries.</p>
|
|
109
|
+
<p><code>read_agents_md</code> serves this file, section by section, over the same MCP connection.</p></div>
|
|
110
|
+
<div class="ctx-body"><details class="ctx-section" id="setup"><summary>Setup</summary><div class="md"><pre><code class="language-bash">npm ci</code></pre>
|
|
111
|
+
<p>Node 22 or newer. No other system dependencies.</p></div></details><details class="ctx-section" id="build"><summary>Build</summary><div class="md"><pre><code class="language-bash">npm run build # tsc -p tsconfig.build.json → dist/
|
|
112
|
+
npm run typecheck # tsc --noEmit over src/ + test/</code></pre></div></details><details class="ctx-section" id="test"><summary>Test</summary><div class="md"><pre><code class="language-bash">npm test # node:test — every test/*.test.ts
|
|
95
113
|
npm run test:coverage # + the coverage gate (lines 90 / funcs 85 / branches 80, src/ only)
|
|
96
114
|
npm run demo # end to end: all tools over stdio, then over stateless HTTP
|
|
97
115
|
npm run version:check # every version-bearing spot agrees with package.json
|
|
98
116
|
npm run faf:check # project.faf / project.fafm / .well-known/fafa still describe the project</code></pre>
|
|
99
|
-
<p>CI runs <code>version:check → faf:check → typecheck → build → test:coverage → demo</code> on Linux, macOS, and Windows for every push and PR to <code>main</code> (<code>.github/workflows/ci.yml</code>), plus <code>catalog:check</code> / <code>card:check</code>, <code>faf-cli check project.faf --strict</code> (the repo dogfoods a <code>project.faf</code> — this keeps it Trophy), and <code>faf:nudge</code> (PR-only, non-blocking — warns if the code's shape moved without <code>project.faf</code>) on Linux.</p>
|
|
100
|
-
<h2 id="layout">Layout</h2>
|
|
101
|
-
<table><thead><tr><th>Path</th><th>What</th></tr></thead><tbody><tr><td><code>src/server.ts</code></td><td>the MCP server — the nine tools + the Server Card resource</td></tr><tr><td><code>src/agents-md.ts</code></td><td>reads and section-splits this file</td></tr><tr><td><code>src/author.ts</code></td><td><code>author_agents_md</code> — BETTER via <code>agents-md-facts</code>, BEST when <code>project.faf</code> exists</td></tr><tr><td><code>src/md.ts</code></td><td>a minimal dependency-free Markdown → HTML renderer</td></tr><tr><td><code>src/render-card.ts</code></td><td>the card — identity + this file + memory + discovery, as one HTML page</td></tr><tr><td><code>src/memory.ts</code> → <code>src/faf/parse-fafm.ts</code></td><td>file-backed <code>remember</code> / <code>recall</code> / <code>forget</code></td></tr><tr><td><code>src/identity.ts</code></td><td><code>whoami</code> (<code>.fafa</code> → <code>package.json</code> fallback) + the <code>_meta</code> context block</td></tr><tr><td><code>src/catalog-gen.ts</code></td><td>writes <code>.well-known/ai-catalog.json</code> from the same three sources</td></tr><tr><td><code>src/transport/http.ts</code></td><td>the stateless Streamable HTTP app (Hono)</td></tr><tr><td><code>src/bin.ts</code></td><td>the entry point (<code>resolveLaunch</code>) — <code>stdio</code> · <code>--http</code> · <code>card</code> · <code>--help</code> · <code>--version</code></td></tr><tr><td><code>src/faf/parse-fafm.ts</code> · <code>parse-fafa.ts</code></td><td>the <code>.fafm</code> / <code>.fafa</code> parsers</td></tr></tbody></table>
|
|
102
|
-
<h2 id="conventions">Conventions</h2>
|
|
103
|
-
<ul><li>TypeScript strict, ESM only (<code>"type": "module"</code>, <code>.js</code> import specifiers).</li><li>Tests use <code>node:test</code> + <code>node:assert/strict</code> — no test framework.</li><li>Every source file opens with a comment stating what it is and why.</li><li>Conventional Commit messages (<code>feat:</code>, <code>fix:</code>, <code>chore:</code>, <code>test:</code>, <code>docs:</code>).</li></ul>
|
|
104
|
-
<h2 id="the-invariant">The invariant</h2>
|
|
105
|
-
<p><code>src/identity.ts</code>, <code>src/catalog-gen.ts</code>, and <code>src/render-card.ts</code> all describe the <strong>same three sources</strong>: this file, <code>project.fafm</code>, <code>.well-known/fafa</code>. Change what one exposes and you must change the others. <code>npm run catalog:check</code> and <code>npm run card:check</code> enforce it in CI — each regenerates its surface and fails on any diff.</p>
|
|
106
|
-
<h2 id="safety">Safety</h2>
|
|
107
|
-
<ul><li>Branch off <code>main</code>; CI must be green before merge.</li><li><code>npm run demo</code> writes a fact to <code>project.fafm</code> and restores the file on exit — don't kill it mid-run.</li><li>No secrets live in this repo; never add any.</li></ul>
|
|
108
|
-
<h2 id="definition-of-done">Definition of done</h2>
|
|
109
|
-
<p><code>npm run typecheck && npm run build && npm test && npm run demo</code> all green, plus <code>npm run catalog:check</code> and <code>npm run card:check</code> clean if you touched <code>AGENTS.md</code>, <code>project.fafm</code>, or <code>.well-known/fafa</code>, and <code>npm run faf:check</code> green if you changed the layout, a dependency, or the identity. On a version bump, <code>npm run version:check</code> green (it lists every spot that must move together) and <code>project.faf</code> still Trophy (<code>faf-cli check project.faf --strict</code>). If CI's <code>faf:nudge</code> warns on your PR, reconcile <code>project.faf</code> (and re-check <code>project.fafm</code> facts if <code>AGENTS.md</code> moved) or say why it's fine.</p>
|
|
110
|
-
<h2 id="authoring-this-file">Authoring this file</h2>
|
|
111
|
-
<p><code>AGENTS.md</code> here is maintained by hand. The <code>author_agents_md</code> tool (or <code>faf export --agents</code>) would draft a BEST version straight from this repo's own <code>project.faf</code> plus its detected facts — the server doesn't care how the file was authored, only that it's valid Markdown.</p></div>
|
|
117
|
+
<p>CI runs <code>version:check → faf:check → typecheck → build → test:coverage → demo</code> on Linux, macOS, and Windows for every push and PR to <code>main</code> (<code>.github/workflows/ci.yml</code>), plus <code>catalog:check</code> / <code>card:check</code>, <code>faf-cli check project.faf --strict</code> (the repo dogfoods a <code>project.faf</code> — this keeps it Trophy), and <code>faf:nudge</code> (PR-only, non-blocking — warns if the code's shape moved without <code>project.faf</code>) on Linux.</p></div></details><details class="ctx-section" id="layout"><summary>Layout</summary><div class="md"><table><thead><tr><th>Path</th><th>What</th></tr></thead><tbody><tr><td><code>src/server.ts</code></td><td>the MCP server — the nine tools + the Server Card resource</td></tr><tr><td><code>src/agents-md.ts</code></td><td>reads and section-splits this file</td></tr><tr><td><code>src/author.ts</code></td><td><code>author_agents_md</code> — BETTER via <code>agents-md-facts</code>, BEST when <code>project.faf</code> exists</td></tr><tr><td><code>src/md.ts</code></td><td>a minimal dependency-free Markdown → HTML renderer</td></tr><tr><td><code>src/render-card.ts</code></td><td>the card — identity + this file + memory + discovery, as one HTML page</td></tr><tr><td><code>src/memory.ts</code> → <code>src/faf/parse-fafm.ts</code></td><td>file-backed <code>remember</code> / <code>recall</code> / <code>forget</code></td></tr><tr><td><code>src/identity.ts</code></td><td><code>whoami</code> (<code>.fafa</code> → <code>package.json</code> fallback) + the <code>_meta</code> context block</td></tr><tr><td><code>src/catalog-gen.ts</code></td><td>writes <code>.well-known/ai-catalog.json</code> from the same three sources</td></tr><tr><td><code>src/transport/http.ts</code></td><td>the stateless Streamable HTTP app (Hono)</td></tr><tr><td><code>src/bin.ts</code></td><td>the entry point (<code>resolveLaunch</code>) — <code>stdio</code> · <code>--http</code> · <code>card</code> · <code>--help</code> · <code>--version</code></td></tr><tr><td><code>src/faf/parse-fafm.ts</code> · <code>parse-fafa.ts</code></td><td>the <code>.fafm</code> / <code>.fafa</code> parsers</td></tr></tbody></table></div></details><details class="ctx-section" id="conventions"><summary>Conventions</summary><div class="md"><ul><li>TypeScript strict, ESM only (<code>"type": "module"</code>, <code>.js</code> import specifiers).</li><li>Tests use <code>node:test</code> + <code>node:assert/strict</code> — no test framework.</li><li>Every source file opens with a comment stating what it is and why.</li><li>Conventional Commit messages (<code>feat:</code>, <code>fix:</code>, <code>chore:</code>, <code>test:</code>, <code>docs:</code>).</li></ul></div></details><details class="ctx-section" id="the-invariant"><summary>The invariant</summary><div class="md"><p><code>src/identity.ts</code>, <code>src/catalog-gen.ts</code>, and <code>src/render-card.ts</code> all describe the <strong>same three sources</strong>: this file, <code>project.fafm</code>, <code>.well-known/fafa</code>. Change what one exposes and you must change the others. <code>npm run catalog:check</code> and <code>npm run card:check</code> enforce it in CI — each regenerates its surface and fails on any diff.</p></div></details><details class="ctx-section" id="safety"><summary>Safety</summary><div class="md"><ul><li>Branch off <code>main</code>; CI must be green before merge.</li><li><code>npm run demo</code> writes a fact to <code>project.fafm</code> and restores the file on exit — don't kill it mid-run.</li><li>No secrets live in this repo; never add any.</li></ul></div></details><details class="ctx-section" id="definition-of-done"><summary>Definition of done</summary><div class="md"><p><code>npm run typecheck && npm run build && npm test && npm run demo</code> all green, plus <code>npm run catalog:check</code> and <code>npm run card:check</code> clean if you touched <code>AGENTS.md</code>, <code>project.fafm</code>, or <code>.well-known/fafa</code>, and <code>npm run faf:check</code> green if you changed the layout, a dependency, or the identity. On a version bump, <code>npm run version:check</code> green (it lists every spot that must move together) and <code>project.faf</code> still Trophy (<code>faf-cli check project.faf --strict</code>). If CI's <code>faf:nudge</code> warns on your PR, reconcile <code>project.faf</code> (and re-check <code>project.fafm</code> facts if <code>AGENTS.md</code> moved) or say why it's fine.</p></div></details><details class="ctx-section" id="authoring-this-file"><summary>Authoring this file</summary><div class="md"><p><code>AGENTS.md</code> here is maintained by hand. The <code>author_agents_md</code> tool (or <code>faf export --agents</code>) would draft a BEST version straight from this repo's own <code>project.faf</code> plus its detected facts — the server doesn't care how the file was authored, only that it's valid Markdown.</p></div></details></div>
|
|
112
118
|
</section>
|
|
113
119
|
<section>
|
|
114
120
|
<p class="label">Memory — 4 facts</p>
|
|
@@ -124,5 +130,25 @@ npm run faf:check # project.faf / project.fafm / .well-known/fafa still desc
|
|
|
124
130
|
</section>
|
|
125
131
|
<div class="foot">mcp-context-card · context card</div>
|
|
126
132
|
</main>
|
|
133
|
+
<script>
|
|
134
|
+
(function(){
|
|
135
|
+
var btn=document.querySelector(".xall");
|
|
136
|
+
var secs=[].slice.call(document.querySelectorAll("details.ctx-section"));
|
|
137
|
+
if(!btn||!secs.length)return;
|
|
138
|
+
var sync=function(){btn.textContent=secs.every(function(d){return d.open})?"Collapse all":"Expand all"};
|
|
139
|
+
btn.hidden=false;
|
|
140
|
+
btn.addEventListener("click",function(){
|
|
141
|
+
var open=!secs.every(function(d){return d.open});
|
|
142
|
+
secs.forEach(function(d){d.open=open});sync();
|
|
143
|
+
});
|
|
144
|
+
secs.forEach(function(d){d.addEventListener("toggle",sync)});
|
|
145
|
+
var openHash=function(){var d=document.getElementById(location.hash.slice(1));if(d&&d.tagName==="DETAILS")d.open=true};
|
|
146
|
+
addEventListener("hashchange",openHash);openHash();
|
|
147
|
+
var pre=[];
|
|
148
|
+
addEventListener("beforeprint",function(){pre=secs.map(function(d){return d.open});secs.forEach(function(d){d.open=true})});
|
|
149
|
+
addEventListener("afterprint",function(){secs.forEach(function(d,i){d.open=pre[i]});sync()});
|
|
150
|
+
sync();
|
|
151
|
+
})();
|
|
152
|
+
</script>
|
|
127
153
|
</body>
|
|
128
154
|
</html>
|
package/docs/index.html
CHANGED
|
@@ -30,7 +30,7 @@ body{margin:0;background:var(--bg);color:var(--fg);
|
|
|
30
30
|
font:15px/1.6 -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Helvetica,Arial,sans-serif;
|
|
31
31
|
padding:40px 18px}
|
|
32
32
|
.card{max-width:760px;margin:0 auto;background:var(--card);border:1px solid var(--line);
|
|
33
|
-
border-radius:14px;overflow:
|
|
33
|
+
border-radius:14px;overflow:clip;box-shadow:var(--card-shadow)}
|
|
34
34
|
.card>*{padding:26px 30px}
|
|
35
35
|
.top{border-top:4px solid var(--accent);border-bottom:1px solid var(--line)}
|
|
36
36
|
h1{margin:0 0 10px;font-size:1.7rem;letter-spacing:-.02em}
|
|
@@ -41,9 +41,31 @@ section{border-bottom:1px solid var(--line)}
|
|
|
41
41
|
section:last-child{border-bottom:0}
|
|
42
42
|
.label{font-size:.7rem;font-weight:700;letter-spacing:.16em;text-transform:uppercase;
|
|
43
43
|
color:var(--accent);margin:0 0 14px}
|
|
44
|
-
.toc{display:flex;flex-wrap:wrap;gap:6px 14px;margin:0
|
|
44
|
+
.toc{display:flex;flex-wrap:wrap;gap:6px 14px;margin:0;padding:0;list-style:none}
|
|
45
45
|
.toc a{font-size:.82rem;color:var(--muted);text-decoration:none}
|
|
46
46
|
.toc a:hover{color:var(--accent)}
|
|
47
|
+
/* ── collapsible context ─────────────────────────────────────────── */
|
|
48
|
+
.ctx-nav{position:sticky;top:0;z-index:3;background:var(--card);
|
|
49
|
+
margin:0 -30px 16px;padding:11px 30px;border-bottom:1px solid var(--line);
|
|
50
|
+
display:flex;flex-wrap:wrap;align-items:flex-start;gap:8px 16px}
|
|
51
|
+
.xall{margin-left:auto;flex:none;font:inherit;font-size:.76rem;font-weight:600;
|
|
52
|
+
white-space:nowrap;padding:3px 11px;border-radius:20px;border:1px solid var(--line);
|
|
53
|
+
background:var(--chip);color:var(--muted);cursor:pointer}
|
|
54
|
+
.xall:hover{color:var(--accent);border-color:var(--accent)}
|
|
55
|
+
.ctx-preamble{padding-bottom:4px}
|
|
56
|
+
details.ctx-section{border-top:1px solid var(--line)}
|
|
57
|
+
details.ctx-section>summary{cursor:pointer;list-style:none;padding:11px 0;
|
|
58
|
+
font-weight:600;font-size:1rem;letter-spacing:-.01em;display:flex;gap:9px}
|
|
59
|
+
details.ctx-section>summary::-webkit-details-marker{display:none}
|
|
60
|
+
details.ctx-section>summary::before{content:"›";color:var(--accent);font-weight:700;
|
|
61
|
+
transition:transform .15s ease}
|
|
62
|
+
details.ctx-section[open]>summary::before{transform:rotate(90deg)}
|
|
63
|
+
details.ctx-section>.md{padding:0 0 16px}
|
|
64
|
+
@media print{
|
|
65
|
+
.ctx-nav{display:none}
|
|
66
|
+
details.ctx-section:not([open])>.md{display:block!important}
|
|
67
|
+
details.ctx-section>summary::before{content:""}
|
|
68
|
+
}
|
|
47
69
|
.md h1,.md h2,.md h3,.md h4{margin:22px 0 8px;font-size:1rem;letter-spacing:-.01em}
|
|
48
70
|
.md h1{font-size:1.15rem}
|
|
49
71
|
.md p{margin:8px 0}
|
|
@@ -78,37 +100,21 @@ section:last-child{border-bottom:0}
|
|
|
78
100
|
<main class="card">
|
|
79
101
|
<div class="top">
|
|
80
102
|
<h1>mcp-context-card</h1>
|
|
81
|
-
<div class="pills"><span class="pill">io.github.Wolfe-Jam</span><span class="pill">v1.
|
|
103
|
+
<div class="pills"><span class="pill">io.github.Wolfe-Jam</span><span class="pill">v1.1.0</span><span class="pill accent">published</span><span class="pill">MIT</span></div>
|
|
82
104
|
</div>
|
|
83
105
|
<section>
|
|
84
106
|
<p class="label">Context — AGENTS.md</p>
|
|
85
|
-
<ul class="toc"><li><a href="#setup">Setup</a></li><li><a href="#build">Build</a></li><li><a href="#test">Test</a></li><li><a href="#layout">Layout</a></li><li><a href="#conventions">Conventions</a></li><li><a href="#the-invariant">The invariant</a></li><li><a href="#safety">Safety</a></li><li><a href="#definition-of-done">Definition of done</a></li><li><a href="#authoring-this-file">Authoring this file</a></li></ul><
|
|
86
|
-
<p><code>
|
|
87
|
-
<
|
|
88
|
-
<pre><code class="language-bash">npm ci</code></pre>
|
|
89
|
-
<p>Node 22 or newer. No other system dependencies.</p>
|
|
90
|
-
|
|
91
|
-
<pre><code class="language-bash">npm run build # tsc -p tsconfig.build.json → dist/
|
|
92
|
-
npm run typecheck # tsc --noEmit over src/ + test/</code></pre>
|
|
93
|
-
<h2 id="test">Test</h2>
|
|
94
|
-
<pre><code class="language-bash">npm test # node:test — every test/*.test.ts
|
|
107
|
+
<div class="ctx-nav"><ul class="toc"><li><a href="#setup">Setup</a></li><li><a href="#build">Build</a></li><li><a href="#test">Test</a></li><li><a href="#layout">Layout</a></li><li><a href="#conventions">Conventions</a></li><li><a href="#the-invariant">The invariant</a></li><li><a href="#safety">Safety</a></li><li><a href="#definition-of-done">Definition of done</a></li><li><a href="#authoring-this-file">Authoring this file</a></li></ul><button type="button" class="xall" hidden>Expand all</button></div>
|
|
108
|
+
<div class="ctx-preamble md"><p><code>mcp-context-card</code> is the essential MCP server for a project's <strong>context</strong> (this file), <strong>memory</strong>, and <strong>identity</strong> — usable as your base MCP, or dropped into any existing MCP server as an extension. Discoverable to any MCP client through the two surfaces already in the ecosystem: the Server Card <code>_meta</code> block and <code>ai-catalog.json</code> sibling entries.</p>
|
|
109
|
+
<p><code>read_agents_md</code> serves this file, section by section, over the same MCP connection.</p></div>
|
|
110
|
+
<div class="ctx-body"><details class="ctx-section" id="setup"><summary>Setup</summary><div class="md"><pre><code class="language-bash">npm ci</code></pre>
|
|
111
|
+
<p>Node 22 or newer. No other system dependencies.</p></div></details><details class="ctx-section" id="build"><summary>Build</summary><div class="md"><pre><code class="language-bash">npm run build # tsc -p tsconfig.build.json → dist/
|
|
112
|
+
npm run typecheck # tsc --noEmit over src/ + test/</code></pre></div></details><details class="ctx-section" id="test"><summary>Test</summary><div class="md"><pre><code class="language-bash">npm test # node:test — every test/*.test.ts
|
|
95
113
|
npm run test:coverage # + the coverage gate (lines 90 / funcs 85 / branches 80, src/ only)
|
|
96
114
|
npm run demo # end to end: all tools over stdio, then over stateless HTTP
|
|
97
115
|
npm run version:check # every version-bearing spot agrees with package.json
|
|
98
116
|
npm run faf:check # project.faf / project.fafm / .well-known/fafa still describe the project</code></pre>
|
|
99
|
-
<p>CI runs <code>version:check → faf:check → typecheck → build → test:coverage → demo</code> on Linux, macOS, and Windows for every push and PR to <code>main</code> (<code>.github/workflows/ci.yml</code>), plus <code>catalog:check</code> / <code>card:check</code>, <code>faf-cli check project.faf --strict</code> (the repo dogfoods a <code>project.faf</code> — this keeps it Trophy), and <code>faf:nudge</code> (PR-only, non-blocking — warns if the code's shape moved without <code>project.faf</code>) on Linux.</p>
|
|
100
|
-
<h2 id="layout">Layout</h2>
|
|
101
|
-
<table><thead><tr><th>Path</th><th>What</th></tr></thead><tbody><tr><td><code>src/server.ts</code></td><td>the MCP server — the nine tools + the Server Card resource</td></tr><tr><td><code>src/agents-md.ts</code></td><td>reads and section-splits this file</td></tr><tr><td><code>src/author.ts</code></td><td><code>author_agents_md</code> — BETTER via <code>agents-md-facts</code>, BEST when <code>project.faf</code> exists</td></tr><tr><td><code>src/md.ts</code></td><td>a minimal dependency-free Markdown → HTML renderer</td></tr><tr><td><code>src/render-card.ts</code></td><td>the card — identity + this file + memory + discovery, as one HTML page</td></tr><tr><td><code>src/memory.ts</code> → <code>src/faf/parse-fafm.ts</code></td><td>file-backed <code>remember</code> / <code>recall</code> / <code>forget</code></td></tr><tr><td><code>src/identity.ts</code></td><td><code>whoami</code> (<code>.fafa</code> → <code>package.json</code> fallback) + the <code>_meta</code> context block</td></tr><tr><td><code>src/catalog-gen.ts</code></td><td>writes <code>.well-known/ai-catalog.json</code> from the same three sources</td></tr><tr><td><code>src/transport/http.ts</code></td><td>the stateless Streamable HTTP app (Hono)</td></tr><tr><td><code>src/bin.ts</code></td><td>the entry point (<code>resolveLaunch</code>) — <code>stdio</code> · <code>--http</code> · <code>card</code> · <code>--help</code> · <code>--version</code></td></tr><tr><td><code>src/faf/parse-fafm.ts</code> · <code>parse-fafa.ts</code></td><td>the <code>.fafm</code> / <code>.fafa</code> parsers</td></tr></tbody></table>
|
|
102
|
-
<h2 id="conventions">Conventions</h2>
|
|
103
|
-
<ul><li>TypeScript strict, ESM only (<code>"type": "module"</code>, <code>.js</code> import specifiers).</li><li>Tests use <code>node:test</code> + <code>node:assert/strict</code> — no test framework.</li><li>Every source file opens with a comment stating what it is and why.</li><li>Conventional Commit messages (<code>feat:</code>, <code>fix:</code>, <code>chore:</code>, <code>test:</code>, <code>docs:</code>).</li></ul>
|
|
104
|
-
<h2 id="the-invariant">The invariant</h2>
|
|
105
|
-
<p><code>src/identity.ts</code>, <code>src/catalog-gen.ts</code>, and <code>src/render-card.ts</code> all describe the <strong>same three sources</strong>: this file, <code>project.fafm</code>, <code>.well-known/fafa</code>. Change what one exposes and you must change the others. <code>npm run catalog:check</code> and <code>npm run card:check</code> enforce it in CI — each regenerates its surface and fails on any diff.</p>
|
|
106
|
-
<h2 id="safety">Safety</h2>
|
|
107
|
-
<ul><li>Branch off <code>main</code>; CI must be green before merge.</li><li><code>npm run demo</code> writes a fact to <code>project.fafm</code> and restores the file on exit — don't kill it mid-run.</li><li>No secrets live in this repo; never add any.</li></ul>
|
|
108
|
-
<h2 id="definition-of-done">Definition of done</h2>
|
|
109
|
-
<p><code>npm run typecheck && npm run build && npm test && npm run demo</code> all green, plus <code>npm run catalog:check</code> and <code>npm run card:check</code> clean if you touched <code>AGENTS.md</code>, <code>project.fafm</code>, or <code>.well-known/fafa</code>, and <code>npm run faf:check</code> green if you changed the layout, a dependency, or the identity. On a version bump, <code>npm run version:check</code> green (it lists every spot that must move together) and <code>project.faf</code> still Trophy (<code>faf-cli check project.faf --strict</code>). If CI's <code>faf:nudge</code> warns on your PR, reconcile <code>project.faf</code> (and re-check <code>project.fafm</code> facts if <code>AGENTS.md</code> moved) or say why it's fine.</p>
|
|
110
|
-
<h2 id="authoring-this-file">Authoring this file</h2>
|
|
111
|
-
<p><code>AGENTS.md</code> here is maintained by hand. The <code>author_agents_md</code> tool (or <code>faf export --agents</code>) would draft a BEST version straight from this repo's own <code>project.faf</code> plus its detected facts — the server doesn't care how the file was authored, only that it's valid Markdown.</p></div>
|
|
117
|
+
<p>CI runs <code>version:check → faf:check → typecheck → build → test:coverage → demo</code> on Linux, macOS, and Windows for every push and PR to <code>main</code> (<code>.github/workflows/ci.yml</code>), plus <code>catalog:check</code> / <code>card:check</code>, <code>faf-cli check project.faf --strict</code> (the repo dogfoods a <code>project.faf</code> — this keeps it Trophy), and <code>faf:nudge</code> (PR-only, non-blocking — warns if the code's shape moved without <code>project.faf</code>) on Linux.</p></div></details><details class="ctx-section" id="layout"><summary>Layout</summary><div class="md"><table><thead><tr><th>Path</th><th>What</th></tr></thead><tbody><tr><td><code>src/server.ts</code></td><td>the MCP server — the nine tools + the Server Card resource</td></tr><tr><td><code>src/agents-md.ts</code></td><td>reads and section-splits this file</td></tr><tr><td><code>src/author.ts</code></td><td><code>author_agents_md</code> — BETTER via <code>agents-md-facts</code>, BEST when <code>project.faf</code> exists</td></tr><tr><td><code>src/md.ts</code></td><td>a minimal dependency-free Markdown → HTML renderer</td></tr><tr><td><code>src/render-card.ts</code></td><td>the card — identity + this file + memory + discovery, as one HTML page</td></tr><tr><td><code>src/memory.ts</code> → <code>src/faf/parse-fafm.ts</code></td><td>file-backed <code>remember</code> / <code>recall</code> / <code>forget</code></td></tr><tr><td><code>src/identity.ts</code></td><td><code>whoami</code> (<code>.fafa</code> → <code>package.json</code> fallback) + the <code>_meta</code> context block</td></tr><tr><td><code>src/catalog-gen.ts</code></td><td>writes <code>.well-known/ai-catalog.json</code> from the same three sources</td></tr><tr><td><code>src/transport/http.ts</code></td><td>the stateless Streamable HTTP app (Hono)</td></tr><tr><td><code>src/bin.ts</code></td><td>the entry point (<code>resolveLaunch</code>) — <code>stdio</code> · <code>--http</code> · <code>card</code> · <code>--help</code> · <code>--version</code></td></tr><tr><td><code>src/faf/parse-fafm.ts</code> · <code>parse-fafa.ts</code></td><td>the <code>.fafm</code> / <code>.fafa</code> parsers</td></tr></tbody></table></div></details><details class="ctx-section" id="conventions"><summary>Conventions</summary><div class="md"><ul><li>TypeScript strict, ESM only (<code>"type": "module"</code>, <code>.js</code> import specifiers).</li><li>Tests use <code>node:test</code> + <code>node:assert/strict</code> — no test framework.</li><li>Every source file opens with a comment stating what it is and why.</li><li>Conventional Commit messages (<code>feat:</code>, <code>fix:</code>, <code>chore:</code>, <code>test:</code>, <code>docs:</code>).</li></ul></div></details><details class="ctx-section" id="the-invariant"><summary>The invariant</summary><div class="md"><p><code>src/identity.ts</code>, <code>src/catalog-gen.ts</code>, and <code>src/render-card.ts</code> all describe the <strong>same three sources</strong>: this file, <code>project.fafm</code>, <code>.well-known/fafa</code>. Change what one exposes and you must change the others. <code>npm run catalog:check</code> and <code>npm run card:check</code> enforce it in CI — each regenerates its surface and fails on any diff.</p></div></details><details class="ctx-section" id="safety"><summary>Safety</summary><div class="md"><ul><li>Branch off <code>main</code>; CI must be green before merge.</li><li><code>npm run demo</code> writes a fact to <code>project.fafm</code> and restores the file on exit — don't kill it mid-run.</li><li>No secrets live in this repo; never add any.</li></ul></div></details><details class="ctx-section" id="definition-of-done"><summary>Definition of done</summary><div class="md"><p><code>npm run typecheck && npm run build && npm test && npm run demo</code> all green, plus <code>npm run catalog:check</code> and <code>npm run card:check</code> clean if you touched <code>AGENTS.md</code>, <code>project.fafm</code>, or <code>.well-known/fafa</code>, and <code>npm run faf:check</code> green if you changed the layout, a dependency, or the identity. On a version bump, <code>npm run version:check</code> green (it lists every spot that must move together) and <code>project.faf</code> still Trophy (<code>faf-cli check project.faf --strict</code>). If CI's <code>faf:nudge</code> warns on your PR, reconcile <code>project.faf</code> (and re-check <code>project.fafm</code> facts if <code>AGENTS.md</code> moved) or say why it's fine.</p></div></details><details class="ctx-section" id="authoring-this-file"><summary>Authoring this file</summary><div class="md"><p><code>AGENTS.md</code> here is maintained by hand. The <code>author_agents_md</code> tool (or <code>faf export --agents</code>) would draft a BEST version straight from this repo's own <code>project.faf</code> plus its detected facts — the server doesn't care how the file was authored, only that it's valid Markdown.</p></div></details></div>
|
|
112
118
|
</section>
|
|
113
119
|
<section>
|
|
114
120
|
<p class="label">Memory — 4 facts</p>
|
|
@@ -124,5 +130,25 @@ npm run faf:check # project.faf / project.fafm / .well-known/fafa still desc
|
|
|
124
130
|
</section>
|
|
125
131
|
<div class="foot">mcp-context-card · context card</div>
|
|
126
132
|
</main>
|
|
133
|
+
<script>
|
|
134
|
+
(function(){
|
|
135
|
+
var btn=document.querySelector(".xall");
|
|
136
|
+
var secs=[].slice.call(document.querySelectorAll("details.ctx-section"));
|
|
137
|
+
if(!btn||!secs.length)return;
|
|
138
|
+
var sync=function(){btn.textContent=secs.every(function(d){return d.open})?"Collapse all":"Expand all"};
|
|
139
|
+
btn.hidden=false;
|
|
140
|
+
btn.addEventListener("click",function(){
|
|
141
|
+
var open=!secs.every(function(d){return d.open});
|
|
142
|
+
secs.forEach(function(d){d.open=open});sync();
|
|
143
|
+
});
|
|
144
|
+
secs.forEach(function(d){d.addEventListener("toggle",sync)});
|
|
145
|
+
var openHash=function(){var d=document.getElementById(location.hash.slice(1));if(d&&d.tagName==="DETAILS")d.open=true};
|
|
146
|
+
addEventListener("hashchange",openHash);openHash();
|
|
147
|
+
var pre=[];
|
|
148
|
+
addEventListener("beforeprint",function(){pre=secs.map(function(d){return d.open});secs.forEach(function(d){d.open=true})});
|
|
149
|
+
addEventListener("afterprint",function(){secs.forEach(function(d,i){d.open=pre[i]});sync()});
|
|
150
|
+
sync();
|
|
151
|
+
})();
|
|
152
|
+
</script>
|
|
127
153
|
</body>
|
|
128
154
|
</html>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-context-card",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"mcpName": "io.github.Wolfe-Jam/mcp-context-card",
|
|
5
5
|
"description": "The essential MCP components for a project's context (AGENTS.md), cross-session memory, and identity — a base MCP on its own, or a drop-in extension for any existing MCP server. Discoverable through the Server Card _meta block and ai-catalog.json sibling entries.",
|
|
6
6
|
"keywords": [
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"./package.json": "./package.json"
|
|
29
29
|
},
|
|
30
30
|
"engines": {
|
|
31
|
-
"node": ">=
|
|
31
|
+
"node": ">=20"
|
|
32
32
|
},
|
|
33
33
|
"files": [
|
|
34
34
|
"dist",
|
|
@@ -46,9 +46,10 @@
|
|
|
46
46
|
"build": "tsc -p tsconfig.build.json",
|
|
47
47
|
"clean": "node -e \"require('node:fs').rmSync('dist',{recursive:true,force:true})\"",
|
|
48
48
|
"version:check": "node scripts/check-versions.mjs",
|
|
49
|
+
"engines:check": "node scripts/check-engines.mjs",
|
|
49
50
|
"faf:check": "node scripts/check-faf-consistency.mjs",
|
|
50
51
|
"faf:nudge": "node scripts/faf-drift-nudge.mjs",
|
|
51
|
-
"prepublishOnly": "npm run clean && npm run version:check && npm run faf:check && npm run build && npm run typecheck && npm test",
|
|
52
|
+
"prepublishOnly": "npm run clean && npm run version:check && npm run engines:check && npm run faf:check && npm run build && npm run typecheck && npm test",
|
|
52
53
|
"start": "node dist/bin.js",
|
|
53
54
|
"start:http": "node dist/bin.js --http",
|
|
54
55
|
"dev": "tsx src/bin.ts",
|
package/server.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "io.github.Wolfe-Jam/mcp-context-card",
|
|
4
4
|
"title": "mcp-context-card",
|
|
5
5
|
"description": "MCP server for a project's context (AGENTS.md), memory, and identity — base or drop-in extension.",
|
|
6
|
-
"version": "1.
|
|
6
|
+
"version": "1.1.0",
|
|
7
7
|
"repository": {
|
|
8
8
|
"url": "https://github.com/Wolfe-Jam/mcp-context-card",
|
|
9
9
|
"source": "github"
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"registryType": "npm",
|
|
14
14
|
"registryBaseUrl": "https://registry.npmjs.org",
|
|
15
15
|
"identifier": "mcp-context-card",
|
|
16
|
-
"version": "1.
|
|
16
|
+
"version": "1.1.0",
|
|
17
17
|
"runtimeHint": "npx",
|
|
18
18
|
"transport": {
|
|
19
19
|
"type": "stdio"
|