@nanisoft/prism-mcp-server 0.4.1 → 0.4.2

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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
2
-
3
- Copyright (c) 2026 NaniSoft
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
1
+ MIT License
2
+
3
+ Copyright (c) 2026 NaniSoft
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,66 +1,66 @@
1
- # @nanisoft/prism-mcp-server
2
-
3
- Prism's read-only MCP tool surface. Eight retrieval tools answer over the
4
- bundled `PrismDocsStore` corpus from the same Cloudflare Worker that serves the
5
- site, at `https://prism.nanisoft.com/mcp`.
6
-
7
- The package is **transport-free**: `createPrismMcpServer(store, options)` returns
8
- an `McpServer`, and the Worker connects it to `createMcpHandler` from the Agents
9
- SDK's MCP server entry. There is one implementation and no stdio binary at
10
- launch; a stdio-only client bridges with `npx mcp-remote
11
- https://prism.nanisoft.com/mcp`.
12
-
13
- ## Commands
14
-
15
- ```sh
16
- pnpm --filter @nanisoft/prism-mcp-server build # stamp the corpus, then tsc into dist/
17
- pnpm --filter @nanisoft/prism-mcp-server test # the Vitest floor (in-memory round-trips)
18
- ```
19
-
20
- `pretest` compiles the package first. The suite reaches the corpus through
21
- `@nanisoft/prism-llms`'s emitted `data.json`.
22
-
23
- ## The eight tools
24
-
25
- Every tool is self-contained, idempotent, stateless and returns Markdown. A
26
- lookup miss is an `isError` result with did-you-mean suggestions and a browse
27
- pointer, never a throw. Every description carries the import rule and the
28
- surface rule.
29
-
30
- | Tool | Arguments | Purpose |
31
- | --- | --- | --- |
32
- | `list_items` | `{ kind?, category? }` | The kind-grouped catalogue index; `category` is Component-only. |
33
- | `get_item_doc` | `{ name, kind? }` | The assembled per-item Markdown plus its footer. |
34
- | `get_item_props` | `{ name, kind? }` | The `## Props` section, or `## Composition` for a Block or Page. |
35
- | `get_item_source` | `{ name, kind? }` | The public import line plus the verbatim demo. |
36
- | `get_theme_doc` | `{ pack?, mode?, group? }` | A pack's resolved values, or one bound scale. |
37
- | `list_pages` | `{}` | The Overview, Foundation, Content and Changelogs page index. |
38
- | `get_page` | `{ url }` | One non-item page as Markdown; a catalogue URL points at `get_item_doc`. |
39
- | `search_docs` | `{ query, kind?, limit? }` | Substring search over names, descriptions and page bodies. |
40
-
41
- `kind` is the closed union `component | block | page`; `get_theme_doc`'s `group`
42
- is `semantic | motion | typography | spacing | shadow | breakpoint | container`.
43
-
44
- ## The store
45
-
46
- `createPrismMcpServer` runs `parsePrismDocsStore` from `@nanisoft/prism-llms` at
47
- its door, so a raw `data.json` is validated once and every transport inherits the
48
- same rejection. The Worker bundles `@nanisoft/prism-llms/data.json` as an ESM
49
- module; there is no `fs`, `fetch`, KV, cache or runtime lookup at isolate scope.
50
-
51
- ## The build stamp
52
-
53
- `scripts/stamp-built.mjs` writes the gitignored `src/generated/built.ts` before
54
- the build. The stamp is derived from the corpus source commit date plus the
55
- corpus version, so the same commit rebuilds to the same value. When git or the
56
- corpus is unavailable it is `undefined`, and `list_items` omits the
57
- `(built ...)` clause rather than fabricating a date. The stamp lives in this lane,
58
- never in the corpus `dist`, so corpus build-twice determinism is untouched.
59
-
60
- ## Auth
61
-
62
- Public read-only in version one: the corpus is public and the tools are pure
63
- reads over an immutable blob. The Cloudflare Access escalation is pre-written in
64
- `apps/site/worker/access.ts` and inert until enabled. It flips when a tool gains
65
- anything non-public: org-internal examples, write-back, telemetry or a per-user
66
- quota.
1
+ # @nanisoft/prism-mcp-server
2
+
3
+ Prism's read-only MCP tool surface. Eight retrieval tools answer over the
4
+ bundled `PrismDocsStore` corpus from the same Cloudflare Worker that serves the
5
+ site, at `https://prism.nanisoft.com/mcp`.
6
+
7
+ The package is **transport-free**: `createPrismMcpServer(store, options)` returns
8
+ an `McpServer`, and the Worker connects it to `createMcpHandler` from the Agents
9
+ SDK's MCP server entry. There is one implementation and no stdio binary at
10
+ launch; a stdio-only client bridges with `npx mcp-remote
11
+ https://prism.nanisoft.com/mcp`.
12
+
13
+ ## Commands
14
+
15
+ ```sh
16
+ pnpm --filter @nanisoft/prism-mcp-server build # stamp the corpus, then tsc into dist/
17
+ pnpm --filter @nanisoft/prism-mcp-server test # the Vitest floor (in-memory round-trips)
18
+ ```
19
+
20
+ `pretest` compiles the package first. The suite reaches the corpus through
21
+ `@nanisoft/prism-llms`'s emitted `data.json`.
22
+
23
+ ## The eight tools
24
+
25
+ Every tool is self-contained, idempotent, stateless and returns Markdown. A
26
+ lookup miss is an `isError` result with did-you-mean suggestions and a browse
27
+ pointer, never a throw. Every description carries the import rule and the
28
+ surface rule.
29
+
30
+ | Tool | Arguments | Purpose |
31
+ | --- | --- | --- |
32
+ | `list_items` | `{ kind?, category? }` | The kind-grouped catalogue index; `category` is Component-only. |
33
+ | `get_item_doc` | `{ name, kind? }` | The assembled per-item Markdown plus its footer. |
34
+ | `get_item_props` | `{ name, kind? }` | The `## Props` section, or `## Composition` for a Block or Page. |
35
+ | `get_item_source` | `{ name, kind? }` | The public import line plus the verbatim demo. |
36
+ | `get_theme_doc` | `{ pack?, mode?, group? }` | A pack's resolved values, or one bound scale. |
37
+ | `list_pages` | `{}` | The Overview, Foundation, Content and Changelogs page index. |
38
+ | `get_page` | `{ url }` | One non-item page as Markdown; a catalogue URL points at `get_item_doc`. |
39
+ | `search_docs` | `{ query, kind?, limit? }` | Substring search over names, descriptions and page bodies. |
40
+
41
+ `kind` is the closed union `component | block | page`; `get_theme_doc`'s `group`
42
+ is `semantic | motion | typography | spacing | shadow | breakpoint | container`.
43
+
44
+ ## The store
45
+
46
+ `createPrismMcpServer` runs `parsePrismDocsStore` from `@nanisoft/prism-llms` at
47
+ its door, so a raw `data.json` is validated once and every transport inherits the
48
+ same rejection. The Worker bundles `@nanisoft/prism-llms/data.json` as an ESM
49
+ module; there is no `fs`, `fetch`, KV, cache or runtime lookup at isolate scope.
50
+
51
+ ## The build stamp
52
+
53
+ `scripts/stamp-built.mjs` writes the gitignored `src/generated/built.ts` before
54
+ the build. The stamp is derived from the corpus source commit date plus the
55
+ corpus version, so the same commit rebuilds to the same value. When git or the
56
+ corpus is unavailable it is `undefined`, and `list_items` omits the
57
+ `(built ...)` clause rather than fabricating a date. The stamp lives in this lane,
58
+ never in the corpus `dist`, so corpus build-twice determinism is untouched.
59
+
60
+ ## Auth
61
+
62
+ Public read-only in version one: the corpus is public and the tools are pure
63
+ reads over an immutable blob. The Cloudflare Access escalation is pre-written in
64
+ `apps/site/worker/access.ts` and inert until enabled. It flips when a tool gains
65
+ anything non-public: org-internal examples, write-back, telemetry or a per-user
66
+ quota.
@@ -32,13 +32,28 @@ compiled output is part of the component package's precompiled `styles.css`.
32
32
 
33
33
  ## Fonts
34
34
 
35
- The documentation site self-hosts Inter under `apps/site/src/fonts/`, loaded
36
- through `next/font/local`. The shipped files are `InterVariable.woff2` and
37
- `InterVariable-Italic.woff2` from the official Inter release 4.1, and the site
38
- is the only consumer. Inter is licensed under the **SIL Open Font License,
39
- Version 1.1**; the license text is committed at
40
- `apps/site/src/fonts/LICENSE.txt`. Inter is copyright The Inter Project Authors
41
- (rsms.me/inter).
35
+ `@nanisoft/prism-ui` redistributes Inter, because its stylesheet ships the
36
+ `@font-face` rules that back the `--font-sans` token. The binaries are the four
37
+ static Latin subsets in `dist/fonts`: `inter-latin-400.woff2`,
38
+ `inter-latin-500.woff2`, `inter-latin-600.woff2` and
39
+ `inter-latin-400-italic.woff2`, together with the licence text as
40
+ `dist/fonts/Inter-OFL.txt`. Nothing else in these packages ships a font, and no
41
+ downstream site loads its own copy: `next/font` and every other font loader are
42
+ absent from the site as well as from the packages, because a site that loads its
43
+ own face stops rendering the face the library ships.
44
+
45
+ The four files are the Latin subset of the official Inter release 4.001 (see the
46
+ `name` table's unique identifier, `4.001;RSMS;...`). The three upright files are
47
+ the static instances that release publishes; the italic is the same release's
48
+ variable italic, instanced at weight 400 and subset to the same 230 codepoints
49
+ as its upright siblings, so the four files cover one character set.
50
+
51
+ Inter is licensed under the **SIL Open Font License, Version 1.1**; the license
52
+ text is committed at `packages/ui/public/fonts/Inter-OFL.txt` and ships beside
53
+ the binaries. Inter is copyright The Inter Project Authors (rsms.me/inter). The
54
+ Open Font License permits redistribution inside a package of this kind, and
55
+ requires the license to travel with the files, which is why it is in `dist/fonts`
56
+ rather than only in this notice.
42
57
 
43
58
  ## Build and development toolchain
44
59
 
@@ -2,5 +2,5 @@
2
2
  // Derived from the corpus source commit date plus the corpus version. The
3
3
  // fallback is undefined (git or the corpus unavailable), which makes
4
4
  // list_items omit the (built ...) clause rather than fabricate a date.
5
- export const BUILT = "2026-09-28";
6
- export const BUILT_CORPUS = "0.7.0";
5
+ export const BUILT = "2026-11-07";
6
+ export const BUILT_CORPUS = "1.3.0";
@@ -2,4 +2,4 @@
2
2
  // Derived from this package's own package.json, which is the single place a
3
3
  // release writes a version. There is no fallback value: an unreadable manifest
4
4
  // throws in the step above rather than stamping a version this package is not.
5
- export const SERVER_VERSION = "0.4.1";
5
+ export const SERVER_VERSION = "0.4.2";
package/dist/render.js CHANGED
@@ -12,15 +12,47 @@ const error = (value) => ({
12
12
  function fence(code, language = 'tsx') {
13
13
  return `\`\`\`${language}\n${code.replace(/\r\n/g, '\n').replace(/\n$/, '')}\n\`\`\``;
14
14
  }
15
+ /**
16
+ * The name an agent is given for a Kind.
17
+ *
18
+ * A lookup rather than a chain of `if`s ending in a default, and the parameter is
19
+ * an `ItemKind` rather than a `string`. The previous version took a string and
20
+ * returned `'Page'` for anything it did not recognise, which is the worst possible
21
+ * shape for this function: a Kind added to the store and the catalogue would be
22
+ * announced to every agent reading the corpus as a Page, and nothing would fail.
23
+ * An agent that is told a live surface is a page will look for navigation, props
24
+ * and a documentation file, and will be wrong about all three.
25
+ *
26
+ * Narrowing the parameter is what makes the map total. A Kind added to `ItemKind`
27
+ * makes this function a compile error rather than a silent mislabel, and that is
28
+ * the entire reason the signature is `ItemKind` and not `string`.
29
+ */
30
+ const KIND_LABELS = {
31
+ component: 'Component',
32
+ block: 'Block',
33
+ page: 'Page',
34
+ /**
35
+ * `Live` is the Kind's discriminator and `Live surface` is what an agent is
36
+ * told, and the two differ deliberately. The other three are nouns that are also
37
+ * their own label, so there was nothing to decide; `live` is an adjective, and an
38
+ * agent handed the bare word `Live` is handed an adjective to reason about. A noun
39
+ * phrase tells it what it is looking at.
40
+ *
41
+ * That also forced the one sentence this label appears in to stop being phrased
42
+ * around a noun, because "the surface of this Live" reads as an error and "of this
43
+ * Live surface" reads as a stutter. The sentence is now "A <label> is made of the
44
+ * exports below", which is true of all four and awkward about none of them.
45
+ */
46
+ live: 'Live surface',
47
+ };
15
48
  function kindLabel(kind) {
16
- if (kind === 'component')
17
- return 'Component';
18
- if (kind === 'block')
19
- return 'Block';
20
- return 'Page';
49
+ return KIND_LABELS[kind];
21
50
  }
22
51
  function countKinds(items) {
23
- const counts = { component: 0, block: 0, page: 0 };
52
+ // Built from the label table rather than written out, for the same reason: a Kind
53
+ // added to `ItemKind` would not be a missing key here, it would be a compile
54
+ // error, which is the outcome worth having.
55
+ const counts = Object.fromEntries(Object.keys(KIND_LABELS).map((kind) => [kind, 0]));
24
56
  for (const item of items)
25
57
  counts[item.kind] += 1;
26
58
  return counts;
@@ -158,7 +190,7 @@ export function renderItemProps(store, args) {
158
190
  return text(`# ${item.name} - props\n\n${item.props}\n\nThis is the complete public Prism API for \`${item.name}\`; internal primitive props are not a consumer contract.\n\nImport rule: ${IMPORT_RULE}`);
159
191
  }
160
192
  if (item.composition) {
161
- return text(`# ${item.name} - composition\n\nThe public surface of this ${kindLabel(item.kind)} is the composition below.\n\n${item.composition}\n\nImport rule: ${IMPORT_RULE}`);
193
+ return text(`# ${item.name} - composition\n\nA ${kindLabel(item.kind)} is made of the exports below.\n\n${item.composition}\n\nImport rule: ${IMPORT_RULE}`);
162
194
  }
163
195
  return error(`No props or composition are recorded for \`${item.name}\`. Use \`get_item_doc\` for its documentation.`);
164
196
  }
package/dist/schemas.d.ts CHANGED
@@ -12,6 +12,7 @@ export declare const listItemsSchema: z.ZodObject<{
12
12
  component: "component";
13
13
  block: "block";
14
14
  page: "page";
15
+ live: "live";
15
16
  }>>;
16
17
  category: z.ZodOptional<z.ZodEnum<{
17
18
  "Call to action": "Call to action";
@@ -29,6 +30,7 @@ export declare const getItemDocSchema: z.ZodObject<{
29
30
  component: "component";
30
31
  block: "block";
31
32
  page: "page";
33
+ live: "live";
32
34
  }>>;
33
35
  }, z.core.$strip>;
34
36
  export declare const getItemPropsSchema: z.ZodObject<{
@@ -37,6 +39,7 @@ export declare const getItemPropsSchema: z.ZodObject<{
37
39
  component: "component";
38
40
  block: "block";
39
41
  page: "page";
42
+ live: "live";
40
43
  }>>;
41
44
  }, z.core.$strip>;
42
45
  export declare const getItemSourceSchema: z.ZodObject<{
@@ -45,6 +48,7 @@ export declare const getItemSourceSchema: z.ZodObject<{
45
48
  component: "component";
46
49
  block: "block";
47
50
  page: "page";
51
+ live: "live";
48
52
  }>>;
49
53
  }, z.core.$strip>;
50
54
  export declare const getThemeDocSchema: z.ZodObject<{
@@ -55,6 +59,8 @@ export declare const getThemeDocSchema: z.ZodObject<{
55
59
  lavender: "lavender";
56
60
  sky: "sky";
57
61
  peach: "peach";
62
+ cyan: "cyan";
63
+ gold: "gold";
58
64
  }>>;
59
65
  mode: z.ZodOptional<z.ZodEnum<{
60
66
  light: "light";
@@ -80,6 +86,7 @@ export declare const searchDocsSchema: z.ZodObject<{
80
86
  component: "component";
81
87
  block: "block";
82
88
  page: "page";
89
+ live: "live";
83
90
  doc: "doc";
84
91
  }>>;
85
92
  limit: z.ZodOptional<z.ZodNumber>;
package/dist/vocab.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- export declare const ITEM_KINDS: readonly ["component", "block", "page"];
1
+ export declare const ITEM_KINDS: readonly ["component", "block", "page", "live"];
2
2
  export declare const COMPONENT_CATEGORIES: readonly ["Call to action", "Forms and inputs", "Feedback", "Layout", "Data display", "Typography", "Miscellaneous"];
3
- export declare const PACKS: readonly ["default", "blush", "mint", "lavender", "sky", "peach"];
3
+ export declare const PACKS: readonly ["default", "blush", "mint", "lavender", "sky", "peach", "cyan", "gold"];
4
4
  export declare const MODES: readonly ["light", "dark"];
5
5
  export declare const SCALE_GROUPS: readonly ["motion", "typography", "spacing", "shadow", "breakpoint", "container"];
6
6
  /** `'doc'` means a non-item page: a guide, a Foundation or a Content page. */
7
- export declare const SEARCH_KINDS: readonly ["component", "block", "page", "doc"];
7
+ export declare const SEARCH_KINDS: readonly ["component", "block", "page", "live", "doc"];
8
8
  /** The `get_theme_doc` group: the pack-by-mode semantic set or one bound scale. */
9
9
  export declare const TOKEN_GROUPS: readonly ["semantic", "motion", "typography", "spacing", "shadow", "breakpoint", "container"];
10
10
  export type ItemKind = (typeof ITEM_KINDS)[number];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nanisoft/prism-mcp-server",
3
- "version": "0.4.1",
3
+ "version": "0.4.2",
4
4
  "type": "module",
5
5
  "description": "Prism's read-only MCP tool surface: eight retrieval tools over the bundled PrismDocsStore corpus. Transport-free: one factory serves the site Worker at /mcp.",
6
6
  "license": "MIT",
@@ -32,7 +32,7 @@
32
32
  "dependencies": {
33
33
  "@modelcontextprotocol/server": "2.0.0",
34
34
  "zod": "^4.2.0",
35
- "@nanisoft/prism-llms": "0.5.1"
35
+ "@nanisoft/prism-llms": "0.5.2"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@modelcontextprotocol/client": "2.0.0",