mcp-context-card 1.3.1 → 1.4.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 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.3.1"
12
+ version: "1.4.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,22 @@
2
2
 
3
3
  All notable changes to this project. Adheres to [Semantic Versioning](https://semver.org).
4
4
 
5
+ ## 1.4.0
6
+
7
+ The card reads the `.fafa` that `faf card init` writes, and shows the agent's ID.
8
+
9
+ - **Identity reads `agent.fafa`.** `faf card init` writes the agent's `.fafa`
10
+ to `./agent.fafa`; the card, `whoami`, the AI Catalog and the served
11
+ `/.well-known/fafa` now read it, and fall back to `.well-known/fafa` as
12
+ before. With both, `agent.fafa` wins.
13
+ - The README's identity line names the `.fafa`, not "its own Server Card"
14
+ (the MCP Server Card is the `_meta` block).
15
+ - **The agent ID shows.** `agent.id` (the `urn:air:…` that `faf card init`
16
+ writes) is read and shown in the card's Discovery section, the Markdown
17
+ card and `whoami`. No `id`, no line.
18
+
19
+ No API change. 140 tests, all green.
20
+
5
21
  ## 1.3.1
6
22
 
7
23
  The registry listing catches up with 1.3.0.
package/README.md CHANGED
@@ -28,7 +28,7 @@ npx mcp-context-card card
28
28
 
29
29
  ![the card's memory section — a real, tagged, verified fact](./docs/img/card-memory.png)
30
30
 
31
- **identity** — what this server is, from its own Server Card.
31
+ **identity** — what this server is, from its own `.fafa` (`agent.fafa`, else `.well-known/fafa`).
32
32
 
33
33
  ![the card's identity — name and pills](./docs/img/card-identity.png)
34
34
 
@@ -70,7 +70,7 @@ It composes:
70
70
 
71
71
  Vendor-free — context is plain Markdown (`AGENTS.md`); the memory and
72
72
  identity formats are swappable examples. It reads and writes only its own
73
- three files (`AGENTS.md`, `project.fafm`, `.well-known/fafa`), plus the
73
+ three files (`AGENTS.md`, `project.fafm`, and `agent.fafa` or `.well-known/fafa`), plus the
74
74
  `context-card.html` it saves on request — no general file access, no shell,
75
75
  no search.
76
76
 
@@ -235,7 +235,7 @@ over both transports:
235
235
  4. **Discovery** — `list_context_sources()`, then the same server over stateless
236
236
  HTTP with its `.well-known` routes and `GET /card`.
237
237
 
238
- 134 tests on Linux, macOS, and Windows, coverage‑gated in CI. Two spawn a real
238
+ 140 tests on Linux, macOS, and Windows, coverage‑gated in CI. Two spawn a real
239
239
  child process and check a remembered fact survives the restart — one against
240
240
  an existing `project.fafm`, one starting from a project that has never had
241
241
  one; another checks the stdio and HTTP tool surfaces match, and another checks
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.3.1\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";
11
+ export declare const HELP = "mcp-context-card 1.4.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.
@@ -13,12 +13,13 @@ import { fileURLToPath, pathToFileURL } from "node:url";
13
13
  import { parseAgentsMd } from "./agents-md.js";
14
14
  import { parseFafm } from "./faf/parse-fafm.js";
15
15
  import { parseFafa } from "./faf/parse-fafa.js";
16
- import { META_NS } from "./identity.js";
16
+ import { META_NS, fafaFile } from "./identity.js";
17
17
  const iana = (t) => `https://www.iana.org/assignments/media-types/${t}`;
18
18
  export function buildCatalog(root) {
19
19
  const agents = parseAgentsMd(join(root, "AGENTS.md"));
20
20
  const fafm = parseFafm(join(root, "project.fafm"));
21
- const fafa = parseFafa(join(root, ".well-known/fafa"));
21
+ const file = fafaFile(root);
22
+ const fafa = file ? parseFafa(file) : null;
22
23
  const host = fafa?.name ?? "mcp-context-card";
23
24
  return {
24
25
  specVersion: "1.0",
@@ -1,7 +1,7 @@
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.3.1";
4
+ export declare const VERSION = "1.4.0";
5
5
  export declare const SERVER_CARD_URI = "mcp-context-card://server-card";
6
6
  /** MCP Apps (io.modelcontextprotocol/ui): the card as an inline UI resource.
7
7
  * A host that supports MCP Apps fetches this resource and renders it in a
package/dist/constants.js CHANGED
@@ -1,7 +1,7 @@
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.3.1";
4
+ export const VERSION = "1.4.0";
5
5
  export const SERVER_CARD_URI = "mcp-context-card://server-card";
6
6
  /** MCP Apps (io.modelcontextprotocol/ui): the card as an inline UI resource.
7
7
  * A host that supports MCP Apps fetches this resource and renders it in a
@@ -17,6 +17,7 @@ export function parseFafa(path) {
17
17
  version: str(doc.version),
18
18
  name: str(agent.name),
19
19
  displayName: str(agent.displayName),
20
+ id: str(agent.id),
20
21
  vendor: str(agent.vendor),
21
22
  agentVersion: str(agent.version),
22
23
  description: str(agent.description),
@@ -26,6 +26,8 @@ export interface AgentIdentity {
26
26
  version?: string;
27
27
  name?: string;
28
28
  displayName?: string;
29
+ /** `agent.id` — globally unique, e.g. `urn:air:example.com:agent:weather` (what `faf card init` writes). */
30
+ id?: string;
29
31
  vendor?: string;
30
32
  agentVersion?: string;
31
33
  description?: string;
@@ -1,9 +1,14 @@
1
1
  import type { AgentIdentity } from "./faf/types.js";
2
2
  /** The `_meta` key namespace — the publisher's, reverse-DNS. */
3
3
  export declare const META_NS = "io.github.Wolfe-Jam.mcp-context-card";
4
+ /** Where a project keeps its `.fafa`, in the order they are read. */
5
+ export declare const FAFA_FILES: readonly ["agent.fafa", ".well-known/fafa"];
6
+ /** The project's `.fafa`: `agent.fafa` (what `faf card init` writes), else
7
+ * `.well-known/fafa`. null when there is neither. */
8
+ export declare function fafaFile(root: string): string | null;
4
9
  export declare function identity(root: string): AgentIdentity | null;
5
10
  /**
6
- * The identity to show: the `.well-known/fafa` card if present — richer and
11
+ * The identity to show: the `.fafa` ({@link fafaFile}) if present — richer and
7
12
  * portable — else a thin one from `package.json` (the common project has no
8
13
  * `.fafa`). null only when neither exists.
9
14
  */
package/dist/identity.js CHANGED
@@ -1,20 +1,34 @@
1
1
  /**
2
- * identity — reads this server's own `.well-known/fafa` and reports it, and
3
- * builds the `_meta` context block for the Server Card.
2
+ * identity — reads this server's own `.fafa` (`agent.fafa`, where `faf card
3
+ * init` writes it, else `.well-known/fafa`) and reports it, and builds the
4
+ * `_meta` context block for the Server Card.
4
5
  *
5
6
  * The `_meta` block is three reverse-DNS-namespaced keys — one per concern
6
7
  * (context, memory, identity) — each naming a source file, its media type,
7
8
  * and (where one exists) its IANA anchor. Same shape a real client reading
8
9
  * the Server Card would consume; see server.ts for where it's emitted.
9
10
  */
10
- import { readFileSync } from "node:fs";
11
+ import { existsSync, readFileSync } from "node:fs";
11
12
  import { join } from "node:path";
12
13
  import { parseFafa } from "./faf/parse-fafa.js";
13
14
  /** The `_meta` key namespace — the publisher's, reverse-DNS. */
14
15
  export const META_NS = "io.github.Wolfe-Jam.mcp-context-card";
15
16
  const iana = (t) => `https://www.iana.org/assignments/media-types/${t}`;
17
+ /** Where a project keeps its `.fafa`, in the order they are read. */
18
+ export const FAFA_FILES = ["agent.fafa", ".well-known/fafa"];
19
+ /** The project's `.fafa`: `agent.fafa` (what `faf card init` writes), else
20
+ * `.well-known/fafa`. null when there is neither. */
21
+ export function fafaFile(root) {
22
+ for (const f of FAFA_FILES) {
23
+ const p = join(root, f);
24
+ if (existsSync(p))
25
+ return p;
26
+ }
27
+ return null;
28
+ }
16
29
  export function identity(root) {
17
- return parseFafa(join(root, ".well-known/fafa"));
30
+ const file = fafaFile(root);
31
+ return file ? parseFafa(file) : null;
18
32
  }
19
33
  /** Fall back to package.json — most projects have no .fafa, but they have this. */
20
34
  function fromPackageJson(root) {
@@ -34,7 +48,7 @@ function fromPackageJson(root) {
34
48
  }
35
49
  }
36
50
  /**
37
- * The identity to show: the `.well-known/fafa` card if present — richer and
51
+ * The identity to show: the `.fafa` ({@link fafaFile}) if present — richer and
38
52
  * portable — else a thin one from `package.json` (the common project has no
39
53
  * `.fafa`). null only when neither exists.
40
54
  */
@@ -45,7 +59,7 @@ export function resolveIdentity(root) {
45
59
  export function whoami(root) {
46
60
  const id = resolveIdentity(root);
47
61
  if (!id)
48
- return "(no .well-known/fafa or package.json found)";
62
+ return "(no agent.fafa, .well-known/fafa or package.json found)";
49
63
  const parts = [
50
64
  id.displayName ?? id.name ?? "(unnamed)",
51
65
  id.agentVersion ? `v${id.agentVersion}` : null,
@@ -53,7 +67,7 @@ export function whoami(root) {
53
67
  id.status ? `status: ${id.status}` : null,
54
68
  id.license ? id.license : null,
55
69
  ].filter(Boolean);
56
- return parts.join(" · ") + (id.description ? `\n${id.description}` : "");
70
+ return parts.join(" · ") + (id.description ? `\n${id.description}` : "") + (id.id ? `\n${id.id}` : "");
57
71
  }
58
72
  /**
59
73
  * The `_meta` context block — one namespaced key per concern. The `context`
@@ -195,7 +195,7 @@ export function renderCard(root, opts = {}) {
195
195
  ${memoryBody}
196
196
  </section>
197
197
  <section>
198
- <p class="label">Discovery</p>
198
+ <p class="label">Discovery</p>${id?.id ? `\n <p class="fetch">Agent ID <code>${escapeHtml(id.id)}</code></p>` : ""}
199
199
  <table class="disc"><thead><tr><th>concern</th><th>source</th><th>media type</th></tr></thead><tbody>${rows}</tbody></table>
200
200
  <p class="fetch">A machine reads this over <b>MCP</b> from the
201
201
  <code>${escapeHtml(SERVER_CARD_URI)}</code> resource; over <b>HTTP</b> also
@@ -294,6 +294,6 @@ export function renderCardText(root, opts = {}) {
294
294
  .join("\n")}${rest ? `\n\n…and ${plural(rest, "more fact")}, in the full card` : ""}`
295
295
  : "**Memory** · no facts yet. Ask your agent to remember something, and it lands here.");
296
296
  const rows = Object.entries(meta).map(([k, v]) => `| ${k.slice(META_NS.length + 1)} | \`${v.source}\` | \`${v.mediaType}\` |`);
297
- out.push(["**Discovery**", "| concern | source | media type |", "|---|---|---|", ...rows].join("\n"));
297
+ out.push(["**Discovery**", ...(id?.id ? [`Agent ID \`${id.id}\``, ""] : []), "| concern | source | media type |", "|---|---|---|", ...rows].join("\n"));
298
298
  return out.join("\n\n");
299
299
  }
@@ -16,11 +16,11 @@
16
16
  * GET /.well-known/fafa — the agent identity card
17
17
  */
18
18
  import { readFileSync } from "node:fs";
19
- import { join } from "node:path";
20
19
  import { Hono } from "hono";
21
20
  import { cors } from "hono/cors";
22
21
  import { WebStandardStreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js";
23
22
  import { buildCatalog } from "../catalog-gen.js";
23
+ import { fafaFile } from "../identity.js";
24
24
  import { createServer, NAME, ROOT, VERSION, serverCard } from "../server.js";
25
25
  import { renderCard, safeAccent } from "../render-card.js";
26
26
  export function httpApp(root = ROOT) {
@@ -47,9 +47,13 @@ export function httpApp(root = ROOT) {
47
47
  c.header("content-type", "application/ai-catalog+json");
48
48
  return c.body(JSON.stringify(buildCatalog(root), null, 2));
49
49
  });
50
+ // Served at the well-known path, read from agent.fafa or .well-known/fafa.
50
51
  app.get("/.well-known/fafa", (c) => {
52
+ const file = fafaFile(root);
53
+ if (!file)
54
+ return c.notFound();
51
55
  c.header("content-type", "application/vnd.fafa+yaml");
52
- return c.body(readFileSync(join(root, ".well-known/fafa"), "utf8"));
56
+ return c.body(readFileSync(file, "utf8"));
53
57
  });
54
58
  // ── The card — the view for people ──────────────────────────────────
55
59
  app.get("/card", (c) => {
@@ -29,7 +29,7 @@ Both return:
29
29
  ```jsonc
30
30
  {
31
31
  "name": "mcp-context-card",
32
- "version": "1.3.1",
32
+ "version": "1.4.0",
33
33
  "_meta": {
34
34
  "io.github.Wolfe-Jam.mcp-context-card/context": {
35
35
  "source": "AGENTS.md",
@@ -100,7 +100,7 @@ details.ctx-section>.md{padding:0 0 16px}
100
100
  <main class="card">
101
101
  <div class="top">
102
102
  <h1>mcp-context-card</h1>
103
- <div class="pills"><span class="pill">io.github.Wolfe-Jam</span><span class="pill">v1.3.1</span><span class="pill accent">published</span><span class="pill">MIT</span></div>
103
+ <div class="pills"><span class="pill">io.github.Wolfe-Jam</span><span class="pill">v1.4.0</span><span class="pill accent">published</span><span class="pill">MIT</span></div>
104
104
  </div>
105
105
  <section>
106
106
  <p class="label">Context — AGENTS.md</p>
@@ -100,7 +100,7 @@ details.ctx-section>.md{padding:0 0 16px}
100
100
  <main class="card">
101
101
  <div class="top">
102
102
  <h1>mcp-context-card</h1>
103
- <div class="pills"><span class="pill">io.github.Wolfe-Jam</span><span class="pill">v1.3.1</span><span class="pill accent">published</span><span class="pill">MIT</span></div>
103
+ <div class="pills"><span class="pill">io.github.Wolfe-Jam</span><span class="pill">v1.4.0</span><span class="pill accent">published</span><span class="pill">MIT</span></div>
104
104
  </div>
105
105
  <section>
106
106
  <p class="label">Context — AGENTS.md</p>
package/docs/card.html CHANGED
@@ -100,7 +100,7 @@ details.ctx-section>.md{padding:0 0 16px}
100
100
  <main class="card">
101
101
  <div class="top">
102
102
  <h1>mcp-context-card</h1>
103
- <div class="pills"><span class="pill">io.github.Wolfe-Jam</span><span class="pill">v1.3.1</span><span class="pill accent">published</span><span class="pill">MIT</span></div>
103
+ <div class="pills"><span class="pill">io.github.Wolfe-Jam</span><span class="pill">v1.4.0</span><span class="pill accent">published</span><span class="pill">MIT</span></div>
104
104
  </div>
105
105
  <section>
106
106
  <p class="label">Context — AGENTS.md</p>
Binary file
Binary file
Binary file
package/docs/index.html CHANGED
@@ -100,7 +100,7 @@ details.ctx-section>.md{padding:0 0 16px}
100
100
  <main class="card">
101
101
  <div class="top">
102
102
  <h1>mcp-context-card</h1>
103
- <div class="pills"><span class="pill">io.github.Wolfe-Jam</span><span class="pill">v1.3.1</span><span class="pill accent">published</span><span class="pill">MIT</span></div>
103
+ <div class="pills"><span class="pill">io.github.Wolfe-Jam</span><span class="pill">v1.4.0</span><span class="pill accent">published</span><span class="pill">MIT</span></div>
104
104
  </div>
105
105
  <section>
106
106
  <p class="label">Context — AGENTS.md</p>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mcp-context-card",
3
- "version": "1.3.1",
3
+ "version": "1.4.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": [
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.3.1",
6
+ "version": "1.4.0",
7
7
  "repository": {
8
8
  "url": "https://github.com/Wolfe-Jam/mcp-context-card",
9
9
  "source": "github"
@@ -14,7 +14,7 @@
14
14
  "registryType": "npm",
15
15
  "registryBaseUrl": "https://registry.npmjs.org",
16
16
  "identifier": "mcp-context-card",
17
- "version": "1.3.1",
17
+ "version": "1.4.0",
18
18
  "runtimeHint": "npx",
19
19
  "transport": {
20
20
  "type": "stdio"