mcp-context-card 1.9.3 → 1.9.5
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/ai-catalog.json +1 -1
- package/.well-known/fafa +1 -1
- package/CHANGELOG.md +36 -0
- package/README.md +22 -7
- package/dist/bin.d.ts +1 -1
- package/dist/card-app.bundle.js +1 -1
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/render-card.js +6 -5
- package/dist/server.d.ts +8 -3
- package/dist/server.js +29 -11
- package/docs/MECHANISMS.md +2 -2
- package/docs/TRANSPORT.md +5 -5
- package/docs/WIRING.md +2 -6
- package/docs/card-dark.html +2 -2
- package/docs/card-light.html +2 -2
- package/docs/card.html +2 -2
- package/docs/img/card-dark.png +0 -0
- package/docs/img/card-identity.png +0 -0
- package/docs/img/card-light.png +0 -0
- package/docs/index.html +2 -2
- package/package.json +1 -1
- package/server.json +2 -2
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"data": {
|
|
12
12
|
"$schema": "https://static.modelcontextprotocol.io/schemas/v1/server-card.schema.json",
|
|
13
13
|
"name": "io.github.Wolfe-Jam/mcp-context-card",
|
|
14
|
-
"version": "1.9.
|
|
14
|
+
"version": "1.9.5",
|
|
15
15
|
"title": "MCP Context Card",
|
|
16
16
|
"description": "MCP server for a project's context (AGENTS.md), memory, and identity — base or drop-in extension.",
|
|
17
17
|
"websiteUrl": "https://github.com/Wolfe-Jam/mcp-context-card",
|
package/.well-known/fafa
CHANGED
|
@@ -13,7 +13,7 @@ agent:
|
|
|
13
13
|
# a deployment for your own project uses your domain.
|
|
14
14
|
id: "urn:air:faf.one:agent:mcp-context-card"
|
|
15
15
|
vendor: "io.github.Wolfe-Jam"
|
|
16
|
-
version: "1.9.
|
|
16
|
+
version: "1.9.5"
|
|
17
17
|
description: >-
|
|
18
18
|
The essential MCP components for a project's context (AGENTS.md),
|
|
19
19
|
cross-session memory, and identity — a base MCP on its own, or a
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,42 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project. Adheres to [Semantic Versioning](https://semver.org).
|
|
4
4
|
|
|
5
|
+
## 1.9.5
|
|
6
|
+
|
|
7
|
+
From a new-user run: goose with the README's own config, on two real repos.
|
|
8
|
+
|
|
9
|
+
- **The config key is `contextcard`.** goose's own keys have no hyphens, and it
|
|
10
|
+
makes `contextcard` from the name "context card". The README, docs and
|
|
11
|
+
examples use it; the Claude Code command is `/contextcard:show`. Your
|
|
12
|
+
existing key keeps working: the name is yours to choose.
|
|
13
|
+
- **goose's first start.** goose can stop waiting while `npx` downloads the
|
|
14
|
+
package ("process quit before initialization"). The README says what to do:
|
|
15
|
+
restart once it has downloaded, or install it once and run
|
|
16
|
+
`mcp-context-card`.
|
|
17
|
+
- **The mini card's monogram shows on a card with no colour.** With no accent
|
|
18
|
+
the circle is drawn in ink, near-white in a dark host, and the white letter
|
|
19
|
+
disappeared. The letter now takes the card's ground colour.
|
|
20
|
+
- **After a drawn card, the model is asked to add nothing or one line.** The
|
|
21
|
+
reply used to list the fallbacks, and models repeated them to the user.
|
|
22
|
+
- **The Markdown card shows the version once,** in the title.
|
|
23
|
+
|
|
24
|
+
## 1.9.4
|
|
25
|
+
|
|
26
|
+
- **Say "show context card".** One prompt, `show`, asks the model to show the
|
|
27
|
+
card; clients that list MCP prompts offer it as a command (Claude Code:
|
|
28
|
+
`/context-card:show`). The model picks the surface: drawn in the chat by
|
|
29
|
+
MCP Apps hosts, Markdown elsewhere. Naming one changes it: *as markdown*,
|
|
30
|
+
*in the browser*.
|
|
31
|
+
- **Seeing the card no longer writes a file.** The instructions sent a client
|
|
32
|
+
without MCP Apps to `save_context_card`, which writes `context-card.html`.
|
|
33
|
+
They now send it to `render_context_card`, which has returned the Markdown
|
|
34
|
+
card there since 1.9.0; `save_context_card` is for when the user asks for
|
|
35
|
+
the browser or a file. The instructions also ask for nothing or one line
|
|
36
|
+
after a drawn card, so the model doesn't repeat it.
|
|
37
|
+
- **The instructions changed for the first time since 1.8.0**, after a goose
|
|
38
|
+
desktop 1.57 load check: 1.52 had stopped loading a build whose
|
|
39
|
+
instructions changed; 1.57 loads one. A test pins the new text.
|
|
40
|
+
|
|
5
41
|
## 1.9.3
|
|
6
42
|
|
|
7
43
|
From outside reviews of #91 and #94 (goose and claude):
|
package/README.md
CHANGED
|
@@ -22,6 +22,13 @@ title, one-liner. Flip it for the back: About, Context, Memory, Discovery.
|
|
|
22
22
|
npx mcp-context-card card
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
**In your AI client**, once the server is added ([Add it to your setup](#add-it-to-your-setup)), say
|
|
26
|
+
**show context card**. The AI picks where it appears: drawn in the chat by
|
|
27
|
+
hosts that support MCP Apps, as Markdown elsewhere. Name a place to choose:
|
|
28
|
+
*show context card as markdown*, *show context card in the browser*. Clients
|
|
29
|
+
that list MCP prompts also offer it as the prompt `show`; in Claude Code type
|
|
30
|
+
`/contextcard:show`.
|
|
31
|
+
|
|
25
32
|
**context** — the project's `AGENTS.md`, served whole or one section at a time.
|
|
26
33
|
|
|
27
34
|

|
|
@@ -61,7 +68,7 @@ say what it is. `mcp-context-card` is those three, done once:
|
|
|
61
68
|
gains context, memory, and identity discovery it didn't have. Nothing to
|
|
62
69
|
migrate; it composes.
|
|
63
70
|
|
|
64
|
-
Ten tools, two discovery surfaces already in the ecosystem (Server Card
|
|
71
|
+
Ten tools, one prompt (`show`), two discovery surfaces already in the ecosystem (Server Card
|
|
65
72
|
`_meta`, `ai-catalog.json`), and a rendered [card](#the-card). MIT, on npm.
|
|
66
73
|
|
|
67
74
|
It composes:
|
|
@@ -133,9 +140,9 @@ server runs locally, and replies with the card as Markdown (identity, the
|
|
|
133
140
|
`AGENTS.md` sections, memory, discovery), a link to the full card, and its
|
|
134
141
|
address to copy. The Markdown is a tl;dr by default: five facts, each cut to
|
|
135
142
|
its first sentence as stored, so it stays small however much a project
|
|
136
|
-
remembers. `detail: "full"` lists every fact whole.
|
|
137
|
-
|
|
138
|
-
when the user
|
|
143
|
+
remembers. `detail: "full"` lists every fact whole. The server's
|
|
144
|
+
instructions send *show context card* to `render_context_card`, and use
|
|
145
|
+
`save_context_card` only when the user asks for the browser or a file.
|
|
139
146
|
|
|
140
147
|
Light, dark, or auto. The colour is the owner's: set it in the project's
|
|
141
148
|
`.fafa` (`metadata.cards.accent: "#0066cc"`), or pass any hex with `--accent`
|
|
@@ -184,7 +191,7 @@ goose, Claude Desktop, Cursor, or any stdio host:
|
|
|
184
191
|
```jsonc
|
|
185
192
|
{
|
|
186
193
|
"mcpServers": {
|
|
187
|
-
"
|
|
194
|
+
"contextcard": {
|
|
188
195
|
"command": "npx",
|
|
189
196
|
"args": ["-y", "mcp-context-card"]
|
|
190
197
|
}
|
|
@@ -218,6 +225,11 @@ official MCP Apps helper, so it reports its size and fits its frame (about
|
|
|
218
225
|
`"env": { "MCP_CONTEXT_CARD_APP": "plain" }`. Everywhere else the card is
|
|
219
226
|
plain either way.
|
|
220
227
|
|
|
228
|
+
In goose, the first start downloads the package, and goose can stop waiting
|
|
229
|
+
before it's ready ("process quit before initialization"). Restart goose once it
|
|
230
|
+
has downloaded, or install it once (`npm i -g mcp-context-card`) and set the
|
|
231
|
+
command to `mcp-context-card`.
|
|
232
|
+
|
|
221
233
|
If `command: "npx"` fails to spawn (`spawn npx ENOENT` — seen on Cursor, whose
|
|
222
234
|
host process doesn't inherit a shell `PATH`), point `command` at `node` and
|
|
223
235
|
the installed `dist/bin.js` instead — see
|
|
@@ -225,7 +237,7 @@ the installed `dist/bin.js` instead — see
|
|
|
225
237
|
in [docs/TRANSPORT.md](./docs/TRANSPORT.md).
|
|
226
238
|
|
|
227
239
|
Extending an MCP you already run: most hosts accept more than one
|
|
228
|
-
`mcpServers` entry — add `
|
|
240
|
+
`mcpServers` entry — add `contextcard` alongside `server-filesystem`,
|
|
229
241
|
`server-git`, or your own, and every agent in that host gains context,
|
|
230
242
|
memory, and identity discovery without anything else changing.
|
|
231
243
|
|
|
@@ -287,6 +299,9 @@ Seven tools only read. `remember` and `forget` write the memory file, and
|
|
|
287
299
|
destructive and a host can ask before running them. Every tool carries a
|
|
288
300
|
title and MCP tool annotations.
|
|
289
301
|
|
|
302
|
+
One prompt, `show`, asks the model to show the context card. Clients that
|
|
303
|
+
list MCP prompts offer it as a command.
|
|
304
|
+
|
|
290
305
|
## The demo
|
|
291
306
|
|
|
292
307
|
`npm run demo` walks through context, memory, identity and discovery, live,
|
|
@@ -300,7 +315,7 @@ over both transports:
|
|
|
300
315
|
4. **Discovery** — `list_context_sources()`, then the same server over stateless
|
|
301
316
|
HTTP with its `.well-known` routes and `GET /card`.
|
|
302
317
|
|
|
303
|
-
|
|
318
|
+
259 tests on Linux, macOS, and Windows, coverage‑gated in CI.
|
|
304
319
|
`npm run wjttc` runs the WJTTC certification suite (seven tiers, from protocol
|
|
305
320
|
and Server Card conformance to stdio/HTTP parity and the shipped package). Two spawn a real
|
|
306
321
|
child process and check a remembered fact survives the restart — one against
|
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.9.
|
|
11
|
+
export declare const HELP = "mcp-context-card 1.9.5\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) --portrait --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 HOST HTTP bind address (default 127.0.0.1, this machine\n only); 0.0.0.0 exposes it, e.g. in a container\n MCP_CONTEXT_CARD_ALLOWED_HOSTS / _ALLOWED_ORIGINS\n comma-separated Host names / browser origins to\n accept besides this machine's own (reverse proxy, web app)\n MCP_CONTEXT_CARD_PUBLISH_MEMORY=1\n HTTP: serve project.fafm, list it in the AI Catalog,\n and show its facts on an exposed /card (off: memory\n is session data). HTTP has no auth\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
|
* The address the HTTP server binds: `HOST`, else 127.0.0.1. Local by default,
|
|
14
14
|
* as the MCP transports spec recommends; a hosted deploy sets HOST=0.0.0.0.
|
package/dist/card-app.bundle.js
CHANGED
|
@@ -100,4 +100,4 @@ Boolean requesting whether a visible border and background is provided by the ho
|
|
|
100
100
|
- omitted: host decides border`)}),RA=p.object({method:p.literal("ui/request-display-mode"),params:p.object({mode:Tc.describe("The display mode being requested.")})}),vE=p.object({mode:Tc.describe("The display mode that was actually set. May differ from requested if not supported.")}).passthrough(),_E=p.union([p.literal("model"),p.literal("app")]).describe("Tool visibility scope - who can access the tool."),PA=p.object({resourceUri:p.string().optional(),visibility:p.array(_E).optional().describe(`Who can access this tool. Default: ["model", "app"]
|
|
101
101
|
- "model": Tool visible to and callable by the agent
|
|
102
102
|
- "app": Tool callable by the app from this server only`),csp:p.never().optional(),permissions:p.never().optional()}),TA=p.object({mimeTypes:p.array(p.string()).optional().describe('Array of supported MIME types for UI resources.\nMust include `"text/html;profile=mcp-app"` for MCP Apps support.')}),EA=p.object({method:p.literal("ui/download-file"),params:p.object({contents:p.array(p.union([zn,wn])).describe("Resource contents to download \u2014 embedded (inline data) or linked (host fetches). Uses standard MCP resource types.")})}),OA=p.object({method:p.literal("ui/message"),params:p.object({role:p.literal("user").describe('Message role, currently only "user" is supported.'),content:p.array(Ht).describe("Message content blocks (text, image, etc.).")})}),CA=p.object({method:p.literal("ui/notifications/sandbox-resource-ready"),params:p.object({html:p.string().describe("HTML content to load into the inner iframe."),sandbox:p.string().optional().describe("Optional override for the inner iframe's sandbox attribute."),csp:jv.optional().describe("CSP configuration from resource metadata."),permissions:Av.optional().describe("Sandbox permissions from resource metadata.")})}),SE=p.object({method:p.literal("ui/notifications/tool-result"),params:jr.describe("Standard MCP tool execution result.")}),az=p.object({toolInfo:p.object({id:wt.optional().describe("JSON-RPC id of the tools/call request."),tool:Nr.describe("Tool definition including name, inputSchema, etc.")}).optional().describe("Metadata of the tool call that instantiated this App."),theme:rE.optional().describe("Current color theme preference."),styles:mE.optional().describe("Style configuration for theming the app."),displayMode:Tc.optional().describe("How the UI is currently displayed."),availableDisplayModes:p.array(Tc).optional().describe("Display modes the host supports."),containerDimensions:p.union([p.object({height:p.number().describe("Fixed container height in pixels.")}),p.object({maxHeight:p.union([p.number(),p.undefined()]).optional().describe("Maximum container height in pixels.")})]).and(p.union([p.object({width:p.number().describe("Fixed container width in pixels.")}),p.object({maxWidth:p.union([p.number(),p.undefined()]).optional().describe("Maximum container width in pixels.")})])).optional().describe(`Container dimensions. Represents the dimensions of the iframe or other
|
|
103
|
-
container holding the app. Specify either width or maxWidth, and either height or maxHeight.`),locale:p.string().optional().describe("User's language and region preference in BCP 47 format."),timeZone:p.string().optional().describe("User's timezone in IANA format."),userAgent:p.string().optional().describe("Host application identifier."),platform:p.union([p.literal("web"),p.literal("desktop"),p.literal("mobile")]).optional().describe("Platform type for responsive design decisions."),deviceCapabilities:p.object({touch:p.boolean().optional().describe("Whether the device supports touch input."),hover:p.boolean().optional().describe("Whether the device supports hover interactions.")}).optional().describe("Device input capabilities."),safeAreaInsets:p.object({top:p.number().describe("Top safe area inset in pixels."),right:p.number().describe("Right safe area inset in pixels."),bottom:p.number().describe("Bottom safe area inset in pixels."),left:p.number().describe("Left safe area inset in pixels.")}).optional().describe("Mobile safe area boundaries in pixels.")}).passthrough(),bE=p.object({method:p.literal("ui/notifications/host-context-changed"),params:az.describe("Partial context update containing only changed fields.")}),NA=p.object({method:p.literal("ui/update-model-context"),params:p.object({content:p.array(Ht).optional().describe("Context content blocks (text, image, etc.)."),structuredContent:p.record(p.string(),p.unknown().describe("Structured content for machine-readable context data.")).optional().describe("Structured content for machine-readable context data.")})}),jA=p.object({method:p.literal("ui/initialize"),params:p.object({appInfo:Ft.describe("App identification (name and version)."),appCapabilities:gE.describe("Features and capabilities this app provides."),protocolVersion:p.string().describe("Protocol version this app supports.")})}),yE=p.object({protocolVersion:p.string().describe('Negotiated protocol version string (e.g., "2025-11-21").'),hostInfo:Ft.describe("Host application identification and version."),hostCapabilities:hE.describe("Features and capabilities provided by the host."),hostContext:az.describe("Rich context about the host environment.")}).passthrough(),$E={target:"draft-2020-12"};async function iz(e,t){let n=e["~standard"];if(n.jsonSchema)return n.jsonSchema[t]($E);throw Error(`Schema (vendor: ${n.vendor}) does not implement Standard JSON Schema (~standard.jsonSchema). Use a library that does (zod v4, ArkType, Valibot) or wrap your schema accordingly.`)}async function oz(e,t,n=""){let o=await e["~standard"].validate(t);if(o.issues){let r=o.issues.map(i=>{let a=i.path?.map(s=>typeof s=="object"?s.key:s).join(".");return a?`${a}: ${i.message}`:i.message}).join("; ");throw Error(n+r)}return o.value}function zE(e,t){let n={...e,...t};return t.experimental!==void 0&&(n.experimental={...e.experimental,...t.experimental}),t.tools!==void 0&&(n.tools={...e.tools,...t.tools}),n}var Ud=class e extends Rc{_appInfo;_appCapabilities;options;_hostCapabilities;_hostInfo;_hostContext;_registeredTools={};_initializedSent=!1;_registeredEvents=new Set;_events=new Ov;_methods=new Cv;setRequestHandler=(...t)=>{let[n,...o]=rz("request",t)??t;this._methods.claim(n,"setRequestHandler"),super.setRequestHandler.call(this,n,...o)};setNotificationHandler=(...t)=>{let[n,...o]=rz("notification",t)??t;this._methods.claim(n,"setNotificationHandler"),super.setNotificationHandler.call(this,n,...o)};removeRequestHandler=t=>{this._methods.release(t),super.removeRequestHandler(t)};removeNotificationHandler=t=>{this._methods.release(t),super.removeNotificationHandler(t)};replaceRequestHandler=(t,...n)=>{this._methods.replace(t),super.setRequestHandler.call(this,t,...n)};_assertInitialized(t){if(this._initializedSent)return;let n=`[ext-apps] App.${t}() called before connect() completed the ui/initialize handshake. Await app.connect() before calling this method, or move data loading to an ontoolresult handler.`;if(this.options?.strict)throw Error(n);console.warn(`${n}. This will throw in a future release.`)}eventSchemas={toolinput:cE,toolinputpartial:lE,toolresult:SE,toolcancelled:uE,hostcontextchanged:bE};static ONE_SHOT_EVENTS=new Set(["toolinput","toolinputpartial","toolresult","toolcancelled"]);_everHadListener=new Set;_assertHandlerTiming(t){if(!e.ONE_SHOT_EVENTS.has(t)||this._everHadListener.has(t)||(this._everHadListener.add(t),!this._initializedSent))return;let n=`[ext-apps] "${String(t)}" handler registered after connect() completed the ui/initialize handshake. The host may have already sent this notification. Register handlers before calling app.connect().`;if(this.options?.strict)throw Error(n);console.warn(n)}setEventHandler(t,n){n&&this._assertHandlerTiming(t),this._ensureEventSlot(t),this._events.setHandler(t,n)}addEventListener(t,n){this._assertHandlerTiming(t),this._ensureEventSlot(t),this._events.addEventListener(t,n)}removeEventListener(t,n){this._events.removeEventListener(t,n)}_ensureEventSlot(t){if(this._registeredEvents.has(t))return;let n=this.eventSchemas[t];if(!n)throw Error(`Unknown event: ${String(t)}`);this.setNotificationHandler(n.shape.method.value,{params:n.shape.params},o=>{let r=o;t==="hostcontextchanged"&&(this._hostContext={...this._hostContext,...r}),this._events.dispatch(t,r)}),this._registeredEvents.add(t)}getEventHandler(t){return this._events.getHandler(t)}warnIfRequestHandlerReplaced(t,n,o){n&&o&&console.warn(`[MCP Apps] ${t} handler replaced. Previous handler will no longer be called.`)}constructor(t,n={},o={autoResize:!0}){super(o),this._appInfo=t,this._appCapabilities=n,this.options=o,this._methods.replace("notifications/cancelled"),this._methods.replace("notifications/progress"),!o.allowUnsafeEval&&p.config({jitless:!0}),this.replaceRequestHandler("ping",r=>(console.log("Received ping:",r.params),{})),this.setEventHandler("hostcontextchanged",void 0)}buildContext(t){return t}registerAppCapabilities(t){if(this.transport)throw Error("Cannot register capabilities after transport is established");this._appCapabilities=zE(this._appCapabilities,t)}registerTool(t,n,o){if(this._registeredTools[t])throw Error(`Tool ${t} is already registered`);let r=this,i=()=>{r._initializedSent&&r._appCapabilities.tools?.listChanged&&r.sendToolListChanged()},a=n.inputSchema!==void 0,s={title:n.title,description:n.description,inputSchema:n.inputSchema,outputSchema:n.outputSchema,annotations:n.annotations,_meta:n._meta,enabled:!0,enable(){this.enabled=!0,i()},disable(){this.enabled=!1,i()},update(c){Object.assign(this,c),i()},remove(){r._registeredTools[t]===s&&(delete r._registeredTools[t],i())},handler:async(c,l)=>{if(!s.enabled)throw Error(`Tool ${t} is disabled`);let d;if(a){let m=s.inputSchema,f=m?await oz(m,c??{},`Invalid input for tool ${t}: `):c??{};d=await o(f,l)}else d=await o(l);return s.outputSchema&&!d.isError&&(d.structuredContent=await oz(s.outputSchema,d.structuredContent,`Invalid output for tool ${t}: `)),d}};return this._registeredTools[t]=s,!this._appCapabilities.tools&&!this.transport&&this.registerAppCapabilities({tools:{listChanged:!0}}),this.ensureToolHandlersInitialized(),i(),s}_toolHandlersInitialized=!1;ensureToolHandlersInitialized(){this._toolHandlersInitialized||(this._toolHandlersInitialized=!0,this.oncalltool=async(t,n)=>{let o=this._registeredTools[t.name];if(!o)throw Error(`Tool ${t.name} not found`);return o.handler(t.arguments,n)},this.onlisttools=async(t,n)=>({tools:await Promise.all(Object.entries(this._registeredTools).filter(([o,r])=>r.enabled).map(async([o,r])=>{let i={name:o,title:r.title,description:r.description,inputSchema:r.inputSchema?await iz(r.inputSchema,"input"):{type:"object",properties:{}}};return r.outputSchema&&(i.outputSchema=await iz(r.outputSchema,"output")),r.annotations&&(i.annotations=r.annotations),r._meta&&(i._meta=r._meta),i}))}))}async sendToolListChanged(t={}){this._assertInitialized("sendToolListChanged"),await this.notification({method:"notifications/tools/list_changed",params:t})}getHostCapabilities(){return this._hostCapabilities}getHostVersion(){return this._hostInfo}getHostContext(){return this._hostContext}get ontoolinput(){return this.getEventHandler("toolinput")}set ontoolinput(t){this.setEventHandler("toolinput",t)}get ontoolinputpartial(){return this.getEventHandler("toolinputpartial")}set ontoolinputpartial(t){this.setEventHandler("toolinputpartial",t)}get ontoolresult(){return this.getEventHandler("toolresult")}set ontoolresult(t){this.setEventHandler("toolresult",t)}get ontoolcancelled(){return this.getEventHandler("toolcancelled")}set ontoolcancelled(t){this.setEventHandler("toolcancelled",t)}get onhostcontextchanged(){return this.getEventHandler("hostcontextchanged")}set onhostcontextchanged(t){this.setEventHandler("hostcontextchanged",t)}_onteardown;get onteardown(){return this._onteardown}set onteardown(t){this.warnIfRequestHandlerReplaced("onteardown",this._onteardown,t),this._onteardown=t,this.replaceRequestHandler("ui/resource-teardown",{params:pE.shape.params,result:fE},(n,o)=>{if(!this._onteardown)throw Error("No onteardown handler set");return this._onteardown(n,o)})}_oncalltool;get oncalltool(){return this._oncalltool}set oncalltool(t){this.warnIfRequestHandlerReplaced("oncalltool",this._oncalltool,t),this._oncalltool=t,this.replaceRequestHandler("tools/call",(n,o)=>{if(!this._oncalltool)throw Error("No oncalltool handler set");return this._oncalltool(n.params,o)})}_onlisttools;get onlisttools(){return this._onlisttools}set onlisttools(t){this.warnIfRequestHandlerReplaced("onlisttools",this._onlisttools,t),this._onlisttools=t,this.replaceRequestHandler("tools/list",(n,o)=>{if(!this._onlisttools)throw Error("No onlisttools handler set");return this._onlisttools(n.params,o)})}assertCapabilityForMethod(t){switch(t){case"sampling/createMessage":if(!this._hostCapabilities?.sampling)throw Error(`Host does not support sampling (required for ${t})`);break}}assertRequestHandlerCapability(t){switch(t){case"tools/call":case"tools/list":if(!this._appCapabilities.tools)throw Error(`Client does not support tool capability (required for ${t})`);return;default:return}}assertNotificationCapability(t){}async callServerTool(t,n){if(this._assertInitialized("callServerTool"),typeof t=="string")throw Error(`callServerTool() expects an object as its first argument, but received a string ("${t}"). Did you mean: callServerTool({ name: "${t}", arguments: { ... } })?`);return await this.request({method:"tools/call",params:t},{onprogress:()=>{},resetTimeoutOnProgress:!0,...n})}async readServerResource(t,n){return this._assertInitialized("readServerResource"),await this.request({method:"resources/read",params:t},n)}async listServerResources(t,n){return this._assertInitialized("listServerResources"),await this.request({method:"resources/list",params:t},n)}async createSamplingMessage(t,n){return this._assertInitialized("createSamplingMessage"),await this.request({method:"sampling/createMessage",params:t},n)}sendMessage(t,n){return this._assertInitialized("sendMessage"),this.request({method:"ui/message",params:t},sE,n)}sendLog(t){return this.notification({method:"notifications/message",params:t})}updateModelContext(t,n){return this._assertInitialized("updateModelContext"),this.request({method:"ui/update-model-context",params:t},Or,n)}openLink(t,n){return this._assertInitialized("openLink"),this.request({method:"ui/open-link",params:t},oE,n)}sendOpenLink=this.openLink;downloadFile(t,n){return this._assertInitialized("downloadFile"),this.request({method:"ui/download-file",params:t},aE,n)}requestTeardown(t={}){return this.notification({method:"ui/notifications/request-teardown",params:t})}requestDisplayMode(t,n){return this._assertInitialized("requestDisplayMode"),this.request({method:"ui/request-display-mode",params:t},vE,n)}sendSizeChanged(t){return this.notification({method:"ui/notifications/size-changed",params:t})}setupSizeChangedNotifications(){let t=!1,n=0,o=0,r=()=>{t||(t=!0,requestAnimationFrame(()=>{t=!1;let a=document.documentElement,s=a.style.height;a.style.height="max-content";let c=Math.ceil(a.getBoundingClientRect().height);a.style.height=s;let l=Math.ceil(window.innerWidth);(l!==n||c!==o)&&(n=l,o=c,this.sendSizeChanged({width:l,height:c}))}))};r();let i=new ResizeObserver(r);return i.observe(document.documentElement),i.observe(document.body),()=>i.disconnect()}async connect(t=new Nv(window.parent,window.parent),n){if(this.transport)throw Error("App is already connected. Call close() before connecting again.");this._initializedSent=!1;try{await super.connect(t);let o=await this.request({method:"ui/initialize",params:{appCapabilities:this._appCapabilities,appInfo:this._appInfo,protocolVersion:eE}},yE,n);if(o===void 0)throw Error(`Server sent invalid initialize result: ${o}`);this._hostCapabilities=o.hostCapabilities,this._hostInfo=o.hostInfo,this._hostContext=o.hostContext,await this.notification({method:"ui/notifications/initialized"}),this._initializedSent=!0,this.options?.autoResize&&this.setupSizeChangedNotifications()}catch(o){throw this.close(),o}}};function wE(e){return e==="inline"||e==="pip"}function Dd(e){let t=document.getElementById("inline-view");if(!t)return;let n=document.documentElement;if(wE(e?.displayMode))n.dataset.view="inline",t.hidden=!1;else{delete n.dataset.view,t.hidden=!0;let a=document.getElementById("inline-saved");a&&(a.hidden=!0)}let r=e?.theme,i=document.body;i?.dataset&&(r==="light"||r==="dark")&&(i.dataset.theme=r)}function Uv(){let e=document.getElementById("inline-saved");e&&(e.hidden=!1)}async function kE(e){if(!e.getHostContext()?.availableDisplayModes?.includes("fullscreen")){Uv();return}try{if((await e.requestDisplayMode({mode:"fullscreen"})).mode!=="fullscreen"){Uv(),Dd(e.getHostContext());return}let o=document.getElementById("inline-saved");o&&(o.hidden=!0),Dd({...e.getHostContext()??{},displayMode:"fullscreen"})}catch{Uv()}}var IE=window.parent;if(IE!==window){let e=new Ud({name:"mcp-context-card",version:"1.9.3"},{availableDisplayModes:["inline","pip","fullscreen"]},{autoResize:!0});e.addEventListener("hostcontextchanged",()=>{Dd(e.getHostContext())}),document.getElementById("open-full-card")?.addEventListener("click",()=>{kE(e)}),e.connect().then(()=>Dd(e.getHostContext())).catch(()=>{})}})();
|
|
103
|
+
container holding the app. Specify either width or maxWidth, and either height or maxHeight.`),locale:p.string().optional().describe("User's language and region preference in BCP 47 format."),timeZone:p.string().optional().describe("User's timezone in IANA format."),userAgent:p.string().optional().describe("Host application identifier."),platform:p.union([p.literal("web"),p.literal("desktop"),p.literal("mobile")]).optional().describe("Platform type for responsive design decisions."),deviceCapabilities:p.object({touch:p.boolean().optional().describe("Whether the device supports touch input."),hover:p.boolean().optional().describe("Whether the device supports hover interactions.")}).optional().describe("Device input capabilities."),safeAreaInsets:p.object({top:p.number().describe("Top safe area inset in pixels."),right:p.number().describe("Right safe area inset in pixels."),bottom:p.number().describe("Bottom safe area inset in pixels."),left:p.number().describe("Left safe area inset in pixels.")}).optional().describe("Mobile safe area boundaries in pixels.")}).passthrough(),bE=p.object({method:p.literal("ui/notifications/host-context-changed"),params:az.describe("Partial context update containing only changed fields.")}),NA=p.object({method:p.literal("ui/update-model-context"),params:p.object({content:p.array(Ht).optional().describe("Context content blocks (text, image, etc.)."),structuredContent:p.record(p.string(),p.unknown().describe("Structured content for machine-readable context data.")).optional().describe("Structured content for machine-readable context data.")})}),jA=p.object({method:p.literal("ui/initialize"),params:p.object({appInfo:Ft.describe("App identification (name and version)."),appCapabilities:gE.describe("Features and capabilities this app provides."),protocolVersion:p.string().describe("Protocol version this app supports.")})}),yE=p.object({protocolVersion:p.string().describe('Negotiated protocol version string (e.g., "2025-11-21").'),hostInfo:Ft.describe("Host application identification and version."),hostCapabilities:hE.describe("Features and capabilities provided by the host."),hostContext:az.describe("Rich context about the host environment.")}).passthrough(),$E={target:"draft-2020-12"};async function iz(e,t){let n=e["~standard"];if(n.jsonSchema)return n.jsonSchema[t]($E);throw Error(`Schema (vendor: ${n.vendor}) does not implement Standard JSON Schema (~standard.jsonSchema). Use a library that does (zod v4, ArkType, Valibot) or wrap your schema accordingly.`)}async function oz(e,t,n=""){let o=await e["~standard"].validate(t);if(o.issues){let r=o.issues.map(i=>{let a=i.path?.map(s=>typeof s=="object"?s.key:s).join(".");return a?`${a}: ${i.message}`:i.message}).join("; ");throw Error(n+r)}return o.value}function zE(e,t){let n={...e,...t};return t.experimental!==void 0&&(n.experimental={...e.experimental,...t.experimental}),t.tools!==void 0&&(n.tools={...e.tools,...t.tools}),n}var Ud=class e extends Rc{_appInfo;_appCapabilities;options;_hostCapabilities;_hostInfo;_hostContext;_registeredTools={};_initializedSent=!1;_registeredEvents=new Set;_events=new Ov;_methods=new Cv;setRequestHandler=(...t)=>{let[n,...o]=rz("request",t)??t;this._methods.claim(n,"setRequestHandler"),super.setRequestHandler.call(this,n,...o)};setNotificationHandler=(...t)=>{let[n,...o]=rz("notification",t)??t;this._methods.claim(n,"setNotificationHandler"),super.setNotificationHandler.call(this,n,...o)};removeRequestHandler=t=>{this._methods.release(t),super.removeRequestHandler(t)};removeNotificationHandler=t=>{this._methods.release(t),super.removeNotificationHandler(t)};replaceRequestHandler=(t,...n)=>{this._methods.replace(t),super.setRequestHandler.call(this,t,...n)};_assertInitialized(t){if(this._initializedSent)return;let n=`[ext-apps] App.${t}() called before connect() completed the ui/initialize handshake. Await app.connect() before calling this method, or move data loading to an ontoolresult handler.`;if(this.options?.strict)throw Error(n);console.warn(`${n}. This will throw in a future release.`)}eventSchemas={toolinput:cE,toolinputpartial:lE,toolresult:SE,toolcancelled:uE,hostcontextchanged:bE};static ONE_SHOT_EVENTS=new Set(["toolinput","toolinputpartial","toolresult","toolcancelled"]);_everHadListener=new Set;_assertHandlerTiming(t){if(!e.ONE_SHOT_EVENTS.has(t)||this._everHadListener.has(t)||(this._everHadListener.add(t),!this._initializedSent))return;let n=`[ext-apps] "${String(t)}" handler registered after connect() completed the ui/initialize handshake. The host may have already sent this notification. Register handlers before calling app.connect().`;if(this.options?.strict)throw Error(n);console.warn(n)}setEventHandler(t,n){n&&this._assertHandlerTiming(t),this._ensureEventSlot(t),this._events.setHandler(t,n)}addEventListener(t,n){this._assertHandlerTiming(t),this._ensureEventSlot(t),this._events.addEventListener(t,n)}removeEventListener(t,n){this._events.removeEventListener(t,n)}_ensureEventSlot(t){if(this._registeredEvents.has(t))return;let n=this.eventSchemas[t];if(!n)throw Error(`Unknown event: ${String(t)}`);this.setNotificationHandler(n.shape.method.value,{params:n.shape.params},o=>{let r=o;t==="hostcontextchanged"&&(this._hostContext={...this._hostContext,...r}),this._events.dispatch(t,r)}),this._registeredEvents.add(t)}getEventHandler(t){return this._events.getHandler(t)}warnIfRequestHandlerReplaced(t,n,o){n&&o&&console.warn(`[MCP Apps] ${t} handler replaced. Previous handler will no longer be called.`)}constructor(t,n={},o={autoResize:!0}){super(o),this._appInfo=t,this._appCapabilities=n,this.options=o,this._methods.replace("notifications/cancelled"),this._methods.replace("notifications/progress"),!o.allowUnsafeEval&&p.config({jitless:!0}),this.replaceRequestHandler("ping",r=>(console.log("Received ping:",r.params),{})),this.setEventHandler("hostcontextchanged",void 0)}buildContext(t){return t}registerAppCapabilities(t){if(this.transport)throw Error("Cannot register capabilities after transport is established");this._appCapabilities=zE(this._appCapabilities,t)}registerTool(t,n,o){if(this._registeredTools[t])throw Error(`Tool ${t} is already registered`);let r=this,i=()=>{r._initializedSent&&r._appCapabilities.tools?.listChanged&&r.sendToolListChanged()},a=n.inputSchema!==void 0,s={title:n.title,description:n.description,inputSchema:n.inputSchema,outputSchema:n.outputSchema,annotations:n.annotations,_meta:n._meta,enabled:!0,enable(){this.enabled=!0,i()},disable(){this.enabled=!1,i()},update(c){Object.assign(this,c),i()},remove(){r._registeredTools[t]===s&&(delete r._registeredTools[t],i())},handler:async(c,l)=>{if(!s.enabled)throw Error(`Tool ${t} is disabled`);let d;if(a){let m=s.inputSchema,f=m?await oz(m,c??{},`Invalid input for tool ${t}: `):c??{};d=await o(f,l)}else d=await o(l);return s.outputSchema&&!d.isError&&(d.structuredContent=await oz(s.outputSchema,d.structuredContent,`Invalid output for tool ${t}: `)),d}};return this._registeredTools[t]=s,!this._appCapabilities.tools&&!this.transport&&this.registerAppCapabilities({tools:{listChanged:!0}}),this.ensureToolHandlersInitialized(),i(),s}_toolHandlersInitialized=!1;ensureToolHandlersInitialized(){this._toolHandlersInitialized||(this._toolHandlersInitialized=!0,this.oncalltool=async(t,n)=>{let o=this._registeredTools[t.name];if(!o)throw Error(`Tool ${t.name} not found`);return o.handler(t.arguments,n)},this.onlisttools=async(t,n)=>({tools:await Promise.all(Object.entries(this._registeredTools).filter(([o,r])=>r.enabled).map(async([o,r])=>{let i={name:o,title:r.title,description:r.description,inputSchema:r.inputSchema?await iz(r.inputSchema,"input"):{type:"object",properties:{}}};return r.outputSchema&&(i.outputSchema=await iz(r.outputSchema,"output")),r.annotations&&(i.annotations=r.annotations),r._meta&&(i._meta=r._meta),i}))}))}async sendToolListChanged(t={}){this._assertInitialized("sendToolListChanged"),await this.notification({method:"notifications/tools/list_changed",params:t})}getHostCapabilities(){return this._hostCapabilities}getHostVersion(){return this._hostInfo}getHostContext(){return this._hostContext}get ontoolinput(){return this.getEventHandler("toolinput")}set ontoolinput(t){this.setEventHandler("toolinput",t)}get ontoolinputpartial(){return this.getEventHandler("toolinputpartial")}set ontoolinputpartial(t){this.setEventHandler("toolinputpartial",t)}get ontoolresult(){return this.getEventHandler("toolresult")}set ontoolresult(t){this.setEventHandler("toolresult",t)}get ontoolcancelled(){return this.getEventHandler("toolcancelled")}set ontoolcancelled(t){this.setEventHandler("toolcancelled",t)}get onhostcontextchanged(){return this.getEventHandler("hostcontextchanged")}set onhostcontextchanged(t){this.setEventHandler("hostcontextchanged",t)}_onteardown;get onteardown(){return this._onteardown}set onteardown(t){this.warnIfRequestHandlerReplaced("onteardown",this._onteardown,t),this._onteardown=t,this.replaceRequestHandler("ui/resource-teardown",{params:pE.shape.params,result:fE},(n,o)=>{if(!this._onteardown)throw Error("No onteardown handler set");return this._onteardown(n,o)})}_oncalltool;get oncalltool(){return this._oncalltool}set oncalltool(t){this.warnIfRequestHandlerReplaced("oncalltool",this._oncalltool,t),this._oncalltool=t,this.replaceRequestHandler("tools/call",(n,o)=>{if(!this._oncalltool)throw Error("No oncalltool handler set");return this._oncalltool(n.params,o)})}_onlisttools;get onlisttools(){return this._onlisttools}set onlisttools(t){this.warnIfRequestHandlerReplaced("onlisttools",this._onlisttools,t),this._onlisttools=t,this.replaceRequestHandler("tools/list",(n,o)=>{if(!this._onlisttools)throw Error("No onlisttools handler set");return this._onlisttools(n.params,o)})}assertCapabilityForMethod(t){switch(t){case"sampling/createMessage":if(!this._hostCapabilities?.sampling)throw Error(`Host does not support sampling (required for ${t})`);break}}assertRequestHandlerCapability(t){switch(t){case"tools/call":case"tools/list":if(!this._appCapabilities.tools)throw Error(`Client does not support tool capability (required for ${t})`);return;default:return}}assertNotificationCapability(t){}async callServerTool(t,n){if(this._assertInitialized("callServerTool"),typeof t=="string")throw Error(`callServerTool() expects an object as its first argument, but received a string ("${t}"). Did you mean: callServerTool({ name: "${t}", arguments: { ... } })?`);return await this.request({method:"tools/call",params:t},{onprogress:()=>{},resetTimeoutOnProgress:!0,...n})}async readServerResource(t,n){return this._assertInitialized("readServerResource"),await this.request({method:"resources/read",params:t},n)}async listServerResources(t,n){return this._assertInitialized("listServerResources"),await this.request({method:"resources/list",params:t},n)}async createSamplingMessage(t,n){return this._assertInitialized("createSamplingMessage"),await this.request({method:"sampling/createMessage",params:t},n)}sendMessage(t,n){return this._assertInitialized("sendMessage"),this.request({method:"ui/message",params:t},sE,n)}sendLog(t){return this.notification({method:"notifications/message",params:t})}updateModelContext(t,n){return this._assertInitialized("updateModelContext"),this.request({method:"ui/update-model-context",params:t},Or,n)}openLink(t,n){return this._assertInitialized("openLink"),this.request({method:"ui/open-link",params:t},oE,n)}sendOpenLink=this.openLink;downloadFile(t,n){return this._assertInitialized("downloadFile"),this.request({method:"ui/download-file",params:t},aE,n)}requestTeardown(t={}){return this.notification({method:"ui/notifications/request-teardown",params:t})}requestDisplayMode(t,n){return this._assertInitialized("requestDisplayMode"),this.request({method:"ui/request-display-mode",params:t},vE,n)}sendSizeChanged(t){return this.notification({method:"ui/notifications/size-changed",params:t})}setupSizeChangedNotifications(){let t=!1,n=0,o=0,r=()=>{t||(t=!0,requestAnimationFrame(()=>{t=!1;let a=document.documentElement,s=a.style.height;a.style.height="max-content";let c=Math.ceil(a.getBoundingClientRect().height);a.style.height=s;let l=Math.ceil(window.innerWidth);(l!==n||c!==o)&&(n=l,o=c,this.sendSizeChanged({width:l,height:c}))}))};r();let i=new ResizeObserver(r);return i.observe(document.documentElement),i.observe(document.body),()=>i.disconnect()}async connect(t=new Nv(window.parent,window.parent),n){if(this.transport)throw Error("App is already connected. Call close() before connecting again.");this._initializedSent=!1;try{await super.connect(t);let o=await this.request({method:"ui/initialize",params:{appCapabilities:this._appCapabilities,appInfo:this._appInfo,protocolVersion:eE}},yE,n);if(o===void 0)throw Error(`Server sent invalid initialize result: ${o}`);this._hostCapabilities=o.hostCapabilities,this._hostInfo=o.hostInfo,this._hostContext=o.hostContext,await this.notification({method:"ui/notifications/initialized"}),this._initializedSent=!0,this.options?.autoResize&&this.setupSizeChangedNotifications()}catch(o){throw this.close(),o}}};function wE(e){return e==="inline"||e==="pip"}function Dd(e){let t=document.getElementById("inline-view");if(!t)return;let n=document.documentElement;if(wE(e?.displayMode))n.dataset.view="inline",t.hidden=!1;else{delete n.dataset.view,t.hidden=!0;let a=document.getElementById("inline-saved");a&&(a.hidden=!0)}let r=e?.theme,i=document.body;i?.dataset&&(r==="light"||r==="dark")&&(i.dataset.theme=r)}function Uv(){let e=document.getElementById("inline-saved");e&&(e.hidden=!1)}async function kE(e){if(!e.getHostContext()?.availableDisplayModes?.includes("fullscreen")){Uv();return}try{if((await e.requestDisplayMode({mode:"fullscreen"})).mode!=="fullscreen"){Uv(),Dd(e.getHostContext());return}let o=document.getElementById("inline-saved");o&&(o.hidden=!0),Dd({...e.getHostContext()??{},displayMode:"fullscreen"})}catch{Uv()}}var IE=window.parent;if(IE!==window){let e=new Ud({name:"mcp-context-card",version:"1.9.5"},{availableDisplayModes:["inline","pip","fullscreen"]},{autoResize:!0});e.addEventListener("hostcontextchanged",()=>{Dd(e.getHostContext())}),document.getElementById("open-full-card")?.addEventListener("click",()=>{kE(e)}),e.connect().then(()=>Dd(e.getHostContext())).catch(()=>{})}})();
|
package/dist/constants.d.ts
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 declare const NAME = "mcp-context-card";
|
|
4
|
-
export declare const VERSION = "1.9.
|
|
4
|
+
export declare const VERSION = "1.9.5";
|
|
5
5
|
export declare const SERVER_CARD_URI = "mcp-context-card://server-card";
|
|
6
6
|
/** The server's registry identity: reverse-DNS, the same as server.json `name`.
|
|
7
7
|
* It is also `serverInfo.name`, so a Server Card never contradicts the live
|
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.9.
|
|
4
|
+
export const VERSION = "1.9.5";
|
|
5
5
|
export const SERVER_CARD_URI = "mcp-context-card://server-card";
|
|
6
6
|
/** The server's registry identity: reverse-DNS, the same as server.json `name`.
|
|
7
7
|
* It is also `serverInfo.name`, so a Server Card never contradicts the live
|
package/dist/render-card.js
CHANGED
|
@@ -20,7 +20,7 @@ import { parseFafm } from "./memory.js";
|
|
|
20
20
|
import { resolveIdentity, serverCardMeta, META_NS } from "./identity.js";
|
|
21
21
|
import { PUBLISH_MEMORY_ENV, SERVER_CARD_URI } from "./constants.js";
|
|
22
22
|
import { escapeHtml, renderInline, renderMarkdown, slug } from "./md.js";
|
|
23
|
-
import { cardInitials, renderBusinessCard } from "agent-business-card";
|
|
23
|
+
import { cardInitials, renderBusinessCard, safeAccent } from "agent-business-card";
|
|
24
24
|
// The renderer lives in agent-business-card; these readers build the card from a project's files.
|
|
25
25
|
export { cardInitials, renderBusinessCard, safeAccent } from "agent-business-card";
|
|
26
26
|
/** AAIF brand orange (aaif.io): this project's own card colour, declared in its `.fafa`. */
|
|
@@ -104,7 +104,7 @@ function countLabel(n, word) {
|
|
|
104
104
|
* and one action. Hidden until a host's display mode is inline or pip.
|
|
105
105
|
* No host, and the script off, keep the full card.
|
|
106
106
|
*/
|
|
107
|
-
function inlineView(card, counts) {
|
|
107
|
+
function inlineView(card, counts, ink = !safeAccent(card.accent)) {
|
|
108
108
|
const kind = card.title ? `<p class="kind">${escapeHtml(card.title)}</p>` : "";
|
|
109
109
|
const line = card.oneLiner ? `<p class="line">${escapeHtml(card.oneLiner)}</p>` : "";
|
|
110
110
|
const strip = `${countLabel(counts.sections, "section")} · ${countLabel(counts.facts, "fact")} · ${counts.identity ? "identity" : "no identity"}`;
|
|
@@ -114,6 +114,7 @@ body[data-theme="dark"] #inline-view{--iv-fg:#fafafa;--iv-bg:#0d0d0d;--iv-muted:
|
|
|
114
114
|
@media (prefers-color-scheme:dark){body:not([data-theme]) #inline-view{--iv-fg:#fafafa;--iv-bg:#0d0d0d;--iv-muted:#9a9aa0;--iv-line:rgba(255,255,255,.16)}}
|
|
115
115
|
#inline-view .row{display:flex;gap:12px;align-items:center}
|
|
116
116
|
#inline-view .mark{width:36px;height:36px;border-radius:50%;display:grid;place-items:center;flex:none;color:#fff;font-weight:800;background:var(--accent,#FF702D)}
|
|
117
|
+
#inline-view .mark.ink{color:var(--iv-bg)}
|
|
117
118
|
#inline-view h2{margin:0;font-size:1.05rem;line-height:1.2;letter-spacing:-.02em}
|
|
118
119
|
#inline-view .kind{margin:2px 0 0;font-size:.72rem;font-weight:700;letter-spacing:.12em;text-transform:uppercase;color:var(--accent,#FF702D)}
|
|
119
120
|
#inline-view .line{margin:8px 0 0;color:var(--iv-muted)}
|
|
@@ -123,7 +124,7 @@ body[data-theme="dark"] #inline-view{--iv-fg:#fafafa;--iv-bg:#0d0d0d;--iv-muted:
|
|
|
123
124
|
html[data-view="inline"] body{padding:8px;background:transparent}
|
|
124
125
|
html[data-view="inline"] body>:not(#inline-view){display:none}
|
|
125
126
|
</style>
|
|
126
|
-
<div class="row"><div class="mark" aria-hidden="true">${escapeHtml(cardInitials(card.name))}</div><div><h2>${escapeHtml(card.name)}</h2>${kind}</div></div>
|
|
127
|
+
<div class="row"><div class="mark${ink ? " ink" : ""}" aria-hidden="true">${escapeHtml(cardInitials(card.name))}</div><div><h2>${escapeHtml(card.name)}</h2>${kind}</div></div>
|
|
127
128
|
${line}
|
|
128
129
|
<p class="counts">${strip}</p>
|
|
129
130
|
<button type="button" id="open-full-card" class="open">Open full card</button>
|
|
@@ -147,7 +148,7 @@ export function renderCard(root, opts = {}) {
|
|
|
147
148
|
*/
|
|
148
149
|
export function renderAppsCard(root, opts = {}) {
|
|
149
150
|
const card = readCard(root, opts);
|
|
150
|
-
const drawn = withInlineView(renderBusinessCard(card, opts), inlineView(card, cardCounts(root)));
|
|
151
|
+
const drawn = withInlineView(renderBusinessCard(card, opts), inlineView(card, cardCounts(root), !(safeAccent(opts.accent) ?? safeAccent(card.accent))));
|
|
151
152
|
return withCardApp(drawn);
|
|
152
153
|
}
|
|
153
154
|
/** The (i) panel for an agent or server card. */
|
|
@@ -306,7 +307,7 @@ export function renderCardText(root, opts = {}) {
|
|
|
306
307
|
out.push(`**${title}**`);
|
|
307
308
|
const pills = [
|
|
308
309
|
id?.vendor && id.vendor !== id.status ? id.vendor : null,
|
|
309
|
-
id?.agentVersion ? `v${id.agentVersion}` : null,
|
|
310
|
+
id?.agentVersion && !title.includes(`v${id.agentVersion}`) ? `v${id.agentVersion}` : null,
|
|
310
311
|
id?.status,
|
|
311
312
|
id?.license,
|
|
312
313
|
].filter(Boolean);
|
package/dist/server.d.ts
CHANGED
|
@@ -42,10 +42,15 @@ export declare const MAX_ROOTS = 64;
|
|
|
42
42
|
* paths (\\host\share, //host/share), which on Windows would reach the
|
|
43
43
|
* network. */
|
|
44
44
|
export declare function rootPath(uri: unknown): string | undefined;
|
|
45
|
-
/** Sent to every client at initialize.
|
|
46
|
-
*
|
|
47
|
-
*
|
|
45
|
+
/** Sent to every client at initialize. Seeing the card is render_context_card
|
|
46
|
+
* (the host draws it, or the reply is the Markdown card). The user names a
|
|
47
|
+
* surface to change that: Markdown, or the browser (save_context_card, the
|
|
48
|
+
* only path that writes a file), so asking to see the card writes nothing.
|
|
49
|
+
* Pinned as text in test/server.test.ts: a change needs a goose desktop load check. */
|
|
48
50
|
export declare const INSTRUCTIONS: string;
|
|
51
|
+
/** The one prompt: `show`. Clients that list prompts offer it as a command
|
|
52
|
+
* (Claude Code: /<server>:show); it asks the model to show the card. */
|
|
53
|
+
export declare const SHOW_PROMPT = "Show the context card.";
|
|
49
54
|
export interface ServerOptions {
|
|
50
55
|
/** Open a saved file for the person. Set only for a local (stdio) server;
|
|
51
56
|
* over HTTP the browser would open on the server, so it stays unset. */
|
package/dist/server.js
CHANGED
|
@@ -91,16 +91,21 @@ export function rootPath(uri) {
|
|
|
91
91
|
return undefined; // a drive path on POSIX
|
|
92
92
|
return resolve(dir);
|
|
93
93
|
}
|
|
94
|
-
/** Sent to every client at initialize.
|
|
95
|
-
*
|
|
96
|
-
*
|
|
94
|
+
/** Sent to every client at initialize. Seeing the card is render_context_card
|
|
95
|
+
* (the host draws it, or the reply is the Markdown card). The user names a
|
|
96
|
+
* surface to change that: Markdown, or the browser (save_context_card, the
|
|
97
|
+
* only path that writes a file), so asking to see the card writes nothing.
|
|
98
|
+
* Pinned as text in test/server.test.ts: a change needs a goose desktop load check. */
|
|
97
99
|
export const INSTRUCTIONS = "This server publishes a project's context (AGENTS.md), memory (project.fafm) and identity (.well-known/fafa). " +
|
|
98
100
|
"Read them with read_agents_md, recall and whoami; list_context_sources says what is published and where. " +
|
|
99
|
-
"
|
|
100
|
-
"
|
|
101
|
-
"
|
|
102
|
-
"
|
|
103
|
-
"
|
|
101
|
+
"To show the user the context card, call render_context_card. In a client that displays MCP Apps the card appears " +
|
|
102
|
+
"in the chat, so add nothing or one line. Anywhere else it returns the card as Markdown: show it as returned. " +
|
|
103
|
+
"When the user names where they want it, follow that: as Markdown or as text, pass format: \"markdown\"; " +
|
|
104
|
+
"in the browser or as a file, call save_context_card, which writes context-card.html into the project, opens it " +
|
|
105
|
+
"when this server runs locally, and returns a link. Never paste the card's HTML into the chat.";
|
|
106
|
+
/** The one prompt: `show`. Clients that list prompts offer it as a command
|
|
107
|
+
* (Claude Code: /<server>:show); it asks the model to show the card. */
|
|
108
|
+
export const SHOW_PROMPT = "Show the context card.";
|
|
104
109
|
/** Theme / accent / expanded from tool arguments, shared by render and save. */
|
|
105
110
|
function cardOptions(args) {
|
|
106
111
|
const theme = args.theme;
|
|
@@ -142,7 +147,7 @@ function hostRendersApps(server, ctx, era) {
|
|
|
142
147
|
export function createServer(root = ROOT, opts = {}) {
|
|
143
148
|
// A JS caller can pass any string: normalise it; an unknown value falls back to the environment.
|
|
144
149
|
const cardApp = toCardApp(opts.cardApp) ?? cardAppFromEnv();
|
|
145
|
-
const server = new Server({ name: REGISTRY_NAME, title: TITLE, version: VERSION }, { capabilities: { tools: {}, resources: {} }, instructions: INSTRUCTIONS });
|
|
150
|
+
const server = new Server({ name: REGISTRY_NAME, title: TITLE, version: VERSION }, { capabilities: { tools: {}, resources: {}, prompts: {} }, instructions: INSTRUCTIONS });
|
|
146
151
|
// ── Which project? ────────────────────────────────────────────────────
|
|
147
152
|
// Resolved per call, not at startup: a client's roots are only known once
|
|
148
153
|
// it has connected, and they can change (goose sends its session's working
|
|
@@ -271,6 +276,19 @@ export function createServer(root = ROOT, opts = {}) {
|
|
|
271
276
|
server.setRequestHandler('resources/templates/list', async () => ({
|
|
272
277
|
resourceTemplates: [],
|
|
273
278
|
}));
|
|
279
|
+
// ── Prompts ─────────────────────────────────────────────────────────
|
|
280
|
+
// One word to teach: `show`. The server name supplies "context card".
|
|
281
|
+
server.setRequestHandler('prompts/list', async () => ({
|
|
282
|
+
prompts: [{ name: "show", title: "Show the context card", description: "Show this project's context card in the chat." }],
|
|
283
|
+
}));
|
|
284
|
+
server.setRequestHandler('prompts/get', async (req) => {
|
|
285
|
+
if (req.params.name !== "show")
|
|
286
|
+
throw new Error(`unknown prompt: ${req.params.name}`);
|
|
287
|
+
return {
|
|
288
|
+
description: "Show this project's context card in the chat.",
|
|
289
|
+
messages: [{ role: "user", content: { type: "text", text: SHOW_PROMPT } }],
|
|
290
|
+
};
|
|
291
|
+
});
|
|
274
292
|
// ── Tools ───────────────────────────────────────────────────────────
|
|
275
293
|
server.setRequestHandler('tools/list', async () => ({
|
|
276
294
|
tools: [
|
|
@@ -476,8 +494,8 @@ export function createServer(root = ROOT, opts = {}) {
|
|
|
476
494
|
const context = c.agents ? `AGENTS.md with ${n(c.sections, "section")}` : "no AGENTS.md yet";
|
|
477
495
|
return text(`Showing the context card for ${c.name} in the chat: ` +
|
|
478
496
|
`${context}, ${n(c.facts, "remembered fact")}, and this server's identity. ` +
|
|
479
|
-
"
|
|
480
|
-
"
|
|
497
|
+
"The card is drawn in the chat. Add nothing, or one line. " +
|
|
498
|
+
"Only if the user says they can't see it: save_context_card, or format: \"markdown\".");
|
|
481
499
|
}
|
|
482
500
|
return text(`${renderCardText(root, { detail: String(args.detail ?? "").trim().toLowerCase() === "full" ? "full" : "tldr" })}\n\n` +
|
|
483
501
|
"_The original card: save_context_card writes context-card.html and, when this server runs locally, opens it in the browser._");
|
package/docs/MECHANISMS.md
CHANGED
|
@@ -32,7 +32,7 @@ Streamable HTTP endpoint at `<origin>/mcp` and its protocol versions):
|
|
|
32
32
|
{
|
|
33
33
|
"$schema": "https://static.modelcontextprotocol.io/schemas/v1/server-card.schema.json",
|
|
34
34
|
"name": "io.github.Wolfe-Jam/mcp-context-card",
|
|
35
|
-
"version": "1.9.
|
|
35
|
+
"version": "1.9.5",
|
|
36
36
|
"title": "MCP Context Card",
|
|
37
37
|
"description": "MCP server for a project's context (AGENTS.md), memory, and identity — base or drop-in extension.",
|
|
38
38
|
"websiteUrl": "https://github.com/Wolfe-Jam/mcp-context-card",
|
|
@@ -85,7 +85,7 @@ publisher lists artifacts, each entry keyed by its **media type** (`type`).
|
|
|
85
85
|
GET /.well-known/ai-catalog.json
|
|
86
86
|
```
|
|
87
87
|
|
|
88
|
-
Served from `https://ctx.example.com` (1.9.
|
|
88
|
+
Served from `https://ctx.example.com` (1.9.5, this repo's own files):
|
|
89
89
|
|
|
90
90
|
```jsonc
|
|
91
91
|
{
|
package/docs/TRANSPORT.md
CHANGED
|
@@ -74,9 +74,9 @@ transport a 2025 client's `initialize` capabilities don't reach the later
|
|
|
74
74
|
gets the Markdown card; it still draws the card from the tool's `_meta.ui`.
|
|
75
75
|
The mini card and that line share one count (`cardCounts`); level-1
|
|
76
76
|
headings, such as the `# AGENTS.md` title, are not sections. The server
|
|
77
|
-
instructions
|
|
78
|
-
desktop 1.52 stopped loading a build whose instructions changed (cause
|
|
79
|
-
found)
|
|
77
|
+
instructions changed in 1.9.5, after a goose desktop 1.57 load check: goose
|
|
78
|
+
desktop 1.52 had stopped loading a build whose instructions changed (cause
|
|
79
|
+
not found; 1.57 loads one). A test pins them, so any change shows as a diff.
|
|
80
80
|
|
|
81
81
|
After `ui/initialize`, `App.getHostContext()` is the host context
|
|
82
82
|
(`McpUiHostContext` in `@modelcontextprotocol/ext-apps`). The fields this
|
|
@@ -109,7 +109,7 @@ so all logging goes to `stderr`.
|
|
|
109
109
|
// claude_desktop_config.json
|
|
110
110
|
{
|
|
111
111
|
"mcpServers": {
|
|
112
|
-
"
|
|
112
|
+
"contextcard": { "command": "npx", "args": ["-y", "mcp-context-card"] }
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
```
|
|
@@ -141,7 +141,7 @@ GET /.well-known/fafa → the agent identity card
|
|
|
141
141
|
```jsonc
|
|
142
142
|
{
|
|
143
143
|
"mcpServers": {
|
|
144
|
-
"
|
|
144
|
+
"contextcard": { "url": "https://your-host.example/mcp" }
|
|
145
145
|
}
|
|
146
146
|
}
|
|
147
147
|
```
|
package/docs/WIRING.md
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// claude_desktop_config.json · ~/.cursor/mcp.json · etc.
|
|
15
15
|
{
|
|
16
16
|
"mcpServers": {
|
|
17
|
-
"
|
|
17
|
+
"contextcard": {
|
|
18
18
|
"command": "npx",
|
|
19
19
|
"args": ["-y", "mcp-context-card"]
|
|
20
20
|
}
|
|
@@ -38,10 +38,6 @@
|
|
|
38
38
|
the arg — e.g. `command: "node"`, `args: ["/path/to/node_modules/mcp-context-card/dist/bin.js"]`
|
|
39
39
|
(or wherever `npm install -g` / your package manager put it; find it with
|
|
40
40
|
`npm root -g` or `which mcp-context-card` after a global install).
|
|
41
|
-
- **`@modelcontextprotocol/inspector` 2.x reports `prompts/list` as `{ "prompts": [] }`** —
|
|
42
|
-
this server declares only `tools` + `resources`, so `prompts/list` returns
|
|
43
|
-
`-32601` on the wire; the current Inspector CLI masks that as an empty list
|
|
44
|
-
(raw JSON-RPC, or Inspector 0.21.x, shows the real `-32601`).
|
|
45
41
|
|
|
46
42
|
### Streamable HTTP (remote)
|
|
47
43
|
|
|
@@ -50,7 +46,7 @@ HOST=0.0.0.0 PORT=8080 npx mcp-context-card # exposed; without HOST it binds 1
|
|
|
50
46
|
```
|
|
51
47
|
|
|
52
48
|
```jsonc
|
|
53
|
-
{ "mcpServers": { "
|
|
49
|
+
{ "mcpServers": { "contextcard": { "url": "https://your-host.example/mcp" } } }
|
|
54
50
|
```
|
|
55
51
|
|
|
56
52
|
Stateless — any replica serves any request, no session store. Rationale in
|
package/docs/card-dark.html
CHANGED
|
@@ -205,7 +205,7 @@ details.ctx-section>.md{padding:0 0 16px}
|
|
|
205
205
|
</details>
|
|
206
206
|
<div class="id">
|
|
207
207
|
<h1>mcp-context-card</h1>
|
|
208
|
-
<p class="title">MCP server · v1.9.
|
|
208
|
+
<p class="title">MCP server · v1.9.5</p>
|
|
209
209
|
<p class="oneliner">The essential MCP components for a project's context (AGENTS.md), cross-session memory, and identity</p>
|
|
210
210
|
</div>
|
|
211
211
|
<div class="foot-front"><span class="domain">faf.one</span><div class="pills"><span class="pill">io.github.Wolfe-Jam</span><span class="pill accent">published</span><span class="pill">MIT</span></div></div>
|
|
@@ -223,7 +223,7 @@ details.ctx-section>.md{padding:0 0 16px}
|
|
|
223
223
|
</div>
|
|
224
224
|
</details>
|
|
225
225
|
<div class="tabbar"><label for="t-about">About</label><label for="t-context">Context</label><label for="t-memory">Memory</label><label for="t-discovery">Discovery</label></div>
|
|
226
|
-
<div class="panes"><div class="pane p-about"><p class="label">About</p><p class="lead">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 a self-published ai-catalog.json.</p><table class="kv"><tbody><tr><th>Agent ID</th><td><code>urn:air:faf.one:agent:mcp-context-card</code></td></tr><tr><th>Publisher</th><td>io.github.Wolfe-Jam</td></tr><tr><th>Version</th><td>v1.9.
|
|
226
|
+
<div class="panes"><div class="pane p-about"><p class="label">About</p><p class="lead">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 a self-published ai-catalog.json.</p><table class="kv"><tbody><tr><th>Agent ID</th><td><code>urn:air:faf.one:agent:mcp-context-card</code></td></tr><tr><th>Publisher</th><td>io.github.Wolfe-Jam</td></tr><tr><th>Version</th><td>v1.9.5</td></tr><tr><th>Status</th><td>published</td></tr><tr><th>License</th><td>MIT</td></tr><tr><th>Package (npm)</th><td><code>mcp-context-card</code></td></tr></tbody></table></div><div class="pane p-context"><p class="label">Context — AGENTS.md</p><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>
|
|
227
227
|
<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>
|
|
228
228
|
<p><code>read_agents_md</code> serves this file, section by section, over the same MCP connection.</p></div>
|
|
229
229
|
<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>
|
package/docs/card-light.html
CHANGED
|
@@ -205,7 +205,7 @@ details.ctx-section>.md{padding:0 0 16px}
|
|
|
205
205
|
</details>
|
|
206
206
|
<div class="id">
|
|
207
207
|
<h1>mcp-context-card</h1>
|
|
208
|
-
<p class="title">MCP server · v1.9.
|
|
208
|
+
<p class="title">MCP server · v1.9.5</p>
|
|
209
209
|
<p class="oneliner">The essential MCP components for a project's context (AGENTS.md), cross-session memory, and identity</p>
|
|
210
210
|
</div>
|
|
211
211
|
<div class="foot-front"><span class="domain">faf.one</span><div class="pills"><span class="pill">io.github.Wolfe-Jam</span><span class="pill accent">published</span><span class="pill">MIT</span></div></div>
|
|
@@ -223,7 +223,7 @@ details.ctx-section>.md{padding:0 0 16px}
|
|
|
223
223
|
</div>
|
|
224
224
|
</details>
|
|
225
225
|
<div class="tabbar"><label for="t-about">About</label><label for="t-context">Context</label><label for="t-memory">Memory</label><label for="t-discovery">Discovery</label></div>
|
|
226
|
-
<div class="panes"><div class="pane p-about"><p class="label">About</p><p class="lead">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 a self-published ai-catalog.json.</p><table class="kv"><tbody><tr><th>Agent ID</th><td><code>urn:air:faf.one:agent:mcp-context-card</code></td></tr><tr><th>Publisher</th><td>io.github.Wolfe-Jam</td></tr><tr><th>Version</th><td>v1.9.
|
|
226
|
+
<div class="panes"><div class="pane p-about"><p class="label">About</p><p class="lead">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 a self-published ai-catalog.json.</p><table class="kv"><tbody><tr><th>Agent ID</th><td><code>urn:air:faf.one:agent:mcp-context-card</code></td></tr><tr><th>Publisher</th><td>io.github.Wolfe-Jam</td></tr><tr><th>Version</th><td>v1.9.5</td></tr><tr><th>Status</th><td>published</td></tr><tr><th>License</th><td>MIT</td></tr><tr><th>Package (npm)</th><td><code>mcp-context-card</code></td></tr></tbody></table></div><div class="pane p-context"><p class="label">Context — AGENTS.md</p><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>
|
|
227
227
|
<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>
|
|
228
228
|
<p><code>read_agents_md</code> serves this file, section by section, over the same MCP connection.</p></div>
|
|
229
229
|
<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>
|
package/docs/card.html
CHANGED
|
@@ -205,7 +205,7 @@ details.ctx-section>.md{padding:0 0 16px}
|
|
|
205
205
|
</details>
|
|
206
206
|
<div class="id">
|
|
207
207
|
<h1>mcp-context-card</h1>
|
|
208
|
-
<p class="title">MCP server · v1.9.
|
|
208
|
+
<p class="title">MCP server · v1.9.5</p>
|
|
209
209
|
<p class="oneliner">The essential MCP components for a project's context (AGENTS.md), cross-session memory, and identity</p>
|
|
210
210
|
</div>
|
|
211
211
|
<div class="foot-front"><span class="domain">faf.one</span><div class="pills"><span class="pill">io.github.Wolfe-Jam</span><span class="pill accent">published</span><span class="pill">MIT</span></div></div>
|
|
@@ -223,7 +223,7 @@ details.ctx-section>.md{padding:0 0 16px}
|
|
|
223
223
|
</div>
|
|
224
224
|
</details>
|
|
225
225
|
<div class="tabbar"><label for="t-about">About</label><label for="t-context">Context</label><label for="t-memory">Memory</label><label for="t-discovery">Discovery</label></div>
|
|
226
|
-
<div class="panes"><div class="pane p-about"><p class="label">About</p><p class="lead">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 a self-published ai-catalog.json.</p><table class="kv"><tbody><tr><th>Agent ID</th><td><code>urn:air:faf.one:agent:mcp-context-card</code></td></tr><tr><th>Publisher</th><td>io.github.Wolfe-Jam</td></tr><tr><th>Version</th><td>v1.9.
|
|
226
|
+
<div class="panes"><div class="pane p-about"><p class="label">About</p><p class="lead">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 a self-published ai-catalog.json.</p><table class="kv"><tbody><tr><th>Agent ID</th><td><code>urn:air:faf.one:agent:mcp-context-card</code></td></tr><tr><th>Publisher</th><td>io.github.Wolfe-Jam</td></tr><tr><th>Version</th><td>v1.9.5</td></tr><tr><th>Status</th><td>published</td></tr><tr><th>License</th><td>MIT</td></tr><tr><th>Package (npm)</th><td><code>mcp-context-card</code></td></tr></tbody></table></div><div class="pane p-context"><p class="label">Context — AGENTS.md</p><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>
|
|
227
227
|
<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>
|
|
228
228
|
<p><code>read_agents_md</code> serves this file, section by section, over the same MCP connection.</p></div>
|
|
229
229
|
<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>
|
package/docs/img/card-dark.png
CHANGED
|
Binary file
|
|
Binary file
|
package/docs/img/card-light.png
CHANGED
|
Binary file
|
package/docs/index.html
CHANGED
|
@@ -205,7 +205,7 @@ details.ctx-section>.md{padding:0 0 16px}
|
|
|
205
205
|
</details>
|
|
206
206
|
<div class="id">
|
|
207
207
|
<h1>mcp-context-card</h1>
|
|
208
|
-
<p class="title">MCP server · v1.9.
|
|
208
|
+
<p class="title">MCP server · v1.9.5</p>
|
|
209
209
|
<p class="oneliner">The essential MCP components for a project's context (AGENTS.md), cross-session memory, and identity</p>
|
|
210
210
|
</div>
|
|
211
211
|
<div class="foot-front"><span class="domain">faf.one</span><div class="pills"><span class="pill">io.github.Wolfe-Jam</span><span class="pill accent">published</span><span class="pill">MIT</span></div></div>
|
|
@@ -223,7 +223,7 @@ details.ctx-section>.md{padding:0 0 16px}
|
|
|
223
223
|
</div>
|
|
224
224
|
</details>
|
|
225
225
|
<div class="tabbar"><label for="t-about">About</label><label for="t-context">Context</label><label for="t-memory">Memory</label><label for="t-discovery">Discovery</label></div>
|
|
226
|
-
<div class="panes"><div class="pane p-about"><p class="label">About</p><p class="lead">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 a self-published ai-catalog.json.</p><table class="kv"><tbody><tr><th>Agent ID</th><td><code>urn:air:faf.one:agent:mcp-context-card</code></td></tr><tr><th>Publisher</th><td>io.github.Wolfe-Jam</td></tr><tr><th>Version</th><td>v1.9.
|
|
226
|
+
<div class="panes"><div class="pane p-about"><p class="label">About</p><p class="lead">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 a self-published ai-catalog.json.</p><table class="kv"><tbody><tr><th>Agent ID</th><td><code>urn:air:faf.one:agent:mcp-context-card</code></td></tr><tr><th>Publisher</th><td>io.github.Wolfe-Jam</td></tr><tr><th>Version</th><td>v1.9.5</td></tr><tr><th>Status</th><td>published</td></tr><tr><th>License</th><td>MIT</td></tr><tr><th>Package (npm)</th><td><code>mcp-context-card</code></td></tr></tbody></table></div><div class="pane p-context"><p class="label">Context — AGENTS.md</p><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>
|
|
227
227
|
<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>
|
|
228
228
|
<p><code>read_agents_md</code> serves this file, section by section, over the same MCP connection.</p></div>
|
|
229
229
|
<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>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-context-card",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.5",
|
|
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.9.
|
|
6
|
+
"version": "1.9.5",
|
|
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.9.
|
|
17
|
+
"version": "1.9.5",
|
|
18
18
|
"runtimeHint": "npx",
|
|
19
19
|
"transport": {
|
|
20
20
|
"type": "stdio"
|