@hashicorp/design-system-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/LICENSE +375 -0
  2. package/README.md +83 -0
  3. package/dist/catalog/loader.js +99 -0
  4. package/dist/catalog/normalize.js +7 -0
  5. package/dist/catalog/project-root.js +47 -0
  6. package/dist/index.js +107 -0
  7. package/dist/prompts/index.js +10 -0
  8. package/dist/prompts/types.js +5 -0
  9. package/dist/resources/completions.js +30 -0
  10. package/dist/resources/components/constants.js +6 -0
  11. package/dist/resources/components/get-component-by-name.js +54 -0
  12. package/dist/resources/components/get-components.js +33 -0
  13. package/dist/resources/components/index.js +11 -0
  14. package/dist/resources/components/utils.js +39 -0
  15. package/dist/resources/define-resource.js +35 -0
  16. package/dist/resources/hds-icons/constants.js +6 -0
  17. package/dist/resources/hds-icons/get-icon-by-name.js +48 -0
  18. package/dist/resources/hds-icons/get-icons.js +33 -0
  19. package/dist/resources/hds-icons/index.js +11 -0
  20. package/dist/resources/hds-icons/utils.js +26 -0
  21. package/dist/resources/index.js +23 -0
  22. package/dist/resources/responses.js +34 -0
  23. package/dist/resources/tokens/constants.js +2 -0
  24. package/dist/resources/tokens/get-token-by-key.js +51 -0
  25. package/dist/resources/tokens/get-tokens.js +33 -0
  26. package/dist/resources/tokens/index.js +11 -0
  27. package/dist/resources/tokens/utils.js +15 -0
  28. package/dist/resources/types.js +5 -0
  29. package/dist/resources/uri.js +15 -0
  30. package/dist/stores/components/index.js +114 -0
  31. package/dist/stores/components/lookup.js +55 -0
  32. package/dist/stores/components/schema.js +37 -0
  33. package/dist/stores/docs/constants.js +163 -0
  34. package/dist/stores/docs/index.js +322 -0
  35. package/dist/stores/docs/lookup.js +148 -0
  36. package/dist/stores/docs/schema.js +60 -0
  37. package/dist/stores/docs/search-index.js +102 -0
  38. package/dist/stores/docs/tokenize.js +52 -0
  39. package/dist/stores/hds-icons/index.js +115 -0
  40. package/dist/stores/hds-icons/lookup.js +43 -0
  41. package/dist/stores/hds-icons/schema.js +31 -0
  42. package/dist/stores/rank.js +129 -0
  43. package/dist/stores/tokens/index.js +105 -0
  44. package/dist/stores/tokens/lookup.js +65 -0
  45. package/dist/stores/tokens/schema.js +42 -0
  46. package/dist/stores/types.js +5 -0
  47. package/dist/tools/components/constants.js +15 -0
  48. package/dist/tools/components/get-component.js +98 -0
  49. package/dist/tools/components/index.js +11 -0
  50. package/dist/tools/components/search-components.js +75 -0
  51. package/dist/tools/components/utils.js +42 -0
  52. package/dist/tools/constants.js +7 -0
  53. package/dist/tools/define-tool.js +17 -0
  54. package/dist/tools/docs/constants.js +33 -0
  55. package/dist/tools/docs/index.js +8 -0
  56. package/dist/tools/docs/read-doc.js +101 -0
  57. package/dist/tools/docs/search-docs.js +143 -0
  58. package/dist/tools/docs/utils.js +82 -0
  59. package/dist/tools/hds-icons/constants.js +5 -0
  60. package/dist/tools/hds-icons/index.js +7 -0
  61. package/dist/tools/hds-icons/search-icons.js +123 -0
  62. package/dist/tools/index.js +19 -0
  63. package/dist/tools/output-schema.js +11 -0
  64. package/dist/tools/responses.js +52 -0
  65. package/dist/tools/search.js +36 -0
  66. package/dist/tools/tokens/constants.js +5 -0
  67. package/dist/tools/tokens/index.js +7 -0
  68. package/dist/tools/tokens/search-tokens.js +144 -0
  69. package/dist/tools/types.js +5 -0
  70. package/dist/types.js +5 -0
  71. package/docs-catalog.json +39778 -0
  72. package/package.json +62 -0
package/dist/index.js ADDED
@@ -0,0 +1,107 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Copyright IBM Corp. 2021, 2026
4
+ * SPDX-License-Identifier: MPL-2.0
5
+ */
6
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
8
+ import { dirname, resolve } from "node:path";
9
+ import { readFileSync } from "node:fs";
10
+ import { fileURLToPath } from "node:url";
11
+ import { registerPrompts } from "./prompts/index.js";
12
+ import { registerResources } from "./resources/index.js";
13
+ import { registerTools } from "./tools/index.js";
14
+ const currentFilePath = fileURLToPath(import.meta.url);
15
+ const currentDirectoryPath = dirname(currentFilePath);
16
+ const packageJsonPath = resolve(currentDirectoryPath, "../package.json");
17
+ const defaultServerVersion = "0.0.0";
18
+ const getServerVersion = () => {
19
+ try {
20
+ const rawPackageJson = readFileSync(packageJsonPath, "utf8");
21
+ const parsedPackageJson = JSON.parse(rawPackageJson);
22
+ if (typeof parsedPackageJson.version === "string") {
23
+ return parsedPackageJson.version;
24
+ }
25
+ }
26
+ catch (error) {
27
+ console.error("Unable to read MCP package version:", error);
28
+ }
29
+ return defaultServerVersion;
30
+ };
31
+ const buildServer = () => {
32
+ const server = new McpServer({
33
+ name: "helios-design-system-mcp",
34
+ version: getServerVersion(),
35
+ });
36
+ return server;
37
+ };
38
+ const installLifecycleHandlers = (server) => {
39
+ let isShuttingDown = false;
40
+ const shutdown = async (reason, error) => {
41
+ if (isShuttingDown) {
42
+ return;
43
+ }
44
+ isShuttingDown = true;
45
+ if (error) {
46
+ process.exitCode = 1;
47
+ console.error(`Shutting down MCP server due to ${reason}:`, error);
48
+ }
49
+ else {
50
+ console.error(`Shutting down MCP server (${reason})`);
51
+ }
52
+ try {
53
+ await server.close();
54
+ console.error("MCP server shutdown complete");
55
+ }
56
+ catch (closeError) {
57
+ process.exitCode = 1;
58
+ console.error("Failed to close MCP server cleanly:", closeError);
59
+ }
60
+ };
61
+ const onSigint = () => {
62
+ void shutdown("SIGINT").finally(() => process.exit(process.exitCode ?? 0));
63
+ };
64
+ const onSigterm = () => {
65
+ void shutdown("SIGTERM").finally(() => process.exit(process.exitCode ?? 0));
66
+ };
67
+ const onUnhandledRejection = (reason) => {
68
+ void shutdown("unhandledRejection", reason).finally(() => process.exit(process.exitCode ?? 1));
69
+ };
70
+ const onUncaughtException = (error) => {
71
+ void shutdown("uncaughtException", error).finally(() => process.exit(process.exitCode ?? 1));
72
+ };
73
+ process.once("SIGINT", onSigint);
74
+ process.once("SIGTERM", onSigterm);
75
+ process.once("unhandledRejection", onUnhandledRejection);
76
+ process.once("uncaughtException", onUncaughtException);
77
+ return {
78
+ shutdown,
79
+ };
80
+ };
81
+ const main = async () => {
82
+ let shutdown;
83
+ try {
84
+ const server = buildServer();
85
+ shutdown = installLifecycleHandlers(server).shutdown;
86
+ registerPrompts(server);
87
+ registerResources(server);
88
+ registerTools(server);
89
+ const transport = new StdioServerTransport();
90
+ await server.connect(transport);
91
+ // STDIO servers must never write to stdout; use stderr for diagnostics.
92
+ console.error("Helios Design System MCP server running on stdio");
93
+ }
94
+ catch (error) {
95
+ if (shutdown) {
96
+ await shutdown("startup-failure", error);
97
+ }
98
+ else {
99
+ console.error("Failed to initialize MCP server:", error);
100
+ }
101
+ throw error;
102
+ }
103
+ };
104
+ void main().catch((error) => {
105
+ console.error("Fatal error in MCP server:", error);
106
+ process.exit(1);
107
+ });
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Copyright IBM Corp. 2021, 2026
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+ const PROMPTS = [];
6
+ export function registerPrompts(server) {
7
+ for (const prompt of PROMPTS) {
8
+ server.registerPrompt(prompt.name, prompt.config, prompt.callback);
9
+ }
10
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Copyright IBM Corp. 2021, 2026
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+ export {};
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Copyright IBM Corp. 2021, 2026
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+ // matching loop for resource template completion callback
6
+ import { normalizeLookupValue } from "../catalog/normalize.js";
7
+ export const completeFromAliases = ({ items, getAliases, getValue, value, }) => {
8
+ const query = normalizeLookupValue(value);
9
+ const matches = [];
10
+ for (const item of items) {
11
+ const aliases = getAliases(item);
12
+ const isMatch = query.length === 0 ||
13
+ aliases.some((alias) => alias.toLowerCase().includes(query));
14
+ if (isMatch) {
15
+ matches.push(getValue(item));
16
+ }
17
+ }
18
+ return matches;
19
+ };
20
+ export const withSafeCompletion = (resourceName, complete) => {
21
+ return (value) => {
22
+ try {
23
+ return complete(value);
24
+ }
25
+ catch (error) {
26
+ console.error(`Resource completion failed (${resourceName}):`, error);
27
+ return [];
28
+ }
29
+ };
30
+ };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Copyright IBM Corp. 2021, 2026
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+ export const COMPONENTS_URI = "hds://components";
6
+ export const COMPONENT_BY_NAME_URI_TEMPLATE = `${COMPONENTS_URI}/{componentName}`;
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Copyright IBM Corp. 2021, 2026
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+ import { completeFromAliases, withSafeCompletion, } from "../completions.js";
6
+ import { defineDetailResource } from "../define-resource.js";
7
+ import { toJsonResourceResponse } from "../responses.js";
8
+ import { buildDetailUri } from "../uri.js";
9
+ import { COMPONENTS_URI, COMPONENT_BY_NAME_URI_TEMPLATE } from "./constants.js";
10
+ import { getOrLoadComponentStore } from "../../stores/components/index.js";
11
+ import { toSerializableComponent } from "./utils.js";
12
+ export const completeComponentNames = (components, value) => {
13
+ return completeFromAliases({
14
+ items: components,
15
+ // the gts class name a consumer imports, `HdsAdvancedTableTh`, alongside the hbs invocation
16
+ // name and the module path, so a partial of any of the three completes
17
+ getAliases: (component) => [
18
+ component.name,
19
+ component.name.replaceAll("::", ""),
20
+ component.modulePath,
21
+ ],
22
+ getValue: (component) => component.name,
23
+ value,
24
+ });
25
+ };
26
+ export const readComponentByNameResource = (store, componentName) => {
27
+ const component = store.getComponentByName(componentName);
28
+ const uri = buildDetailUri(COMPONENTS_URI, componentName);
29
+ if (component === null) {
30
+ return toJsonResourceResponse(uri, {
31
+ found: false,
32
+ requestedComponentName: componentName,
33
+ message: "Component not found for provided componentName.",
34
+ });
35
+ }
36
+ return toJsonResourceResponse(uri, {
37
+ found: true,
38
+ requestedComponentName: componentName,
39
+ component: toSerializableComponent(component),
40
+ });
41
+ };
42
+ export const createGetComponentByNameResource = (getStore) => {
43
+ return defineDetailResource({
44
+ name: "get_hds_component",
45
+ uriTemplate: COMPONENT_BY_NAME_URI_TEMPLATE,
46
+ title: "HDS component detail",
47
+ description: "Detailed component record for a specific component name",
48
+ variableName: "componentName",
49
+ complete: withSafeCompletion("get_hds_component", (value) => completeComponentNames(getStore().listComponents(), value)),
50
+ read: (componentName) => readComponentByNameResource(getStore(), componentName),
51
+ });
52
+ };
53
+ const getComponentByNameResource = createGetComponentByNameResource(getOrLoadComponentStore);
54
+ export default getComponentByNameResource;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Copyright IBM Corp. 2021, 2026
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+ import { toJsonResourceResponse } from "../responses.js";
6
+ import { COMPONENTS_URI } from "./constants.js";
7
+ import { getOrLoadComponentStore } from "../../stores/components/index.js";
8
+ import { toSerializableComponentSummary } from "./utils.js";
9
+ export const readComponentsResource = (store) => {
10
+ const meta = store.getMeta();
11
+ const payload = {
12
+ totalComponentCount: meta.totalComponentCount,
13
+ source: meta.source,
14
+ components: store
15
+ .listComponents()
16
+ .map((component) => toSerializableComponentSummary(component)),
17
+ };
18
+ return toJsonResourceResponse(COMPONENTS_URI, payload);
19
+ };
20
+ export const createGetComponentsResource = (getStore) => {
21
+ return {
22
+ name: "get_hds_components",
23
+ uri: COMPONENTS_URI,
24
+ config: {
25
+ title: "HDS component catalog index",
26
+ description: "Canonical list of components with summary metadata",
27
+ mimeType: "application/json",
28
+ },
29
+ readCallback: async () => readComponentsResource(getStore()),
30
+ };
31
+ };
32
+ export const getComponentsResource = createGetComponentsResource(getOrLoadComponentStore);
33
+ export default getComponentsResource;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Copyright IBM Corp. 2021, 2026
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+ import getComponentByNameResource from "./get-component-by-name.js";
6
+ import getComponentsResource from "./get-components.js";
7
+ const COMPONENTS_RESOURCES = [
8
+ getComponentsResource,
9
+ getComponentByNameResource,
10
+ ];
11
+ export default COMPONENTS_RESOURCES;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Copyright IBM Corp. 2021, 2026
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+ export const toSerializableComponentSummary = (component) => {
6
+ return {
7
+ name: component.name,
8
+ modulePath: component.modulePath,
9
+ ...(component.docsPath === undefined
10
+ ? {}
11
+ : { docsPath: component.docsPath }),
12
+ };
13
+ };
14
+ export const toSerializableComponent = (component) => {
15
+ return {
16
+ name: component.name,
17
+ modulePath: component.modulePath,
18
+ ...(component.docsPath === undefined
19
+ ? {}
20
+ : { docsPath: component.docsPath }),
21
+ ...(component.element === undefined ? {} : { element: component.element }),
22
+ args: component.args.map((arg) => ({
23
+ name: arg.name,
24
+ type: arg.type,
25
+ required: arg.required,
26
+ ...(arg.values === undefined ? {} : { values: arg.values }),
27
+ ...(arg.inheritedFrom === undefined
28
+ ? {}
29
+ : { inheritedFrom: arg.inheritedFrom }),
30
+ })),
31
+ blocks: component.blocks.map((block) => ({
32
+ name: block.name,
33
+ yields: block.yields.map((blockYield) => ({
34
+ name: blockYield.name,
35
+ type: blockYield.type,
36
+ })),
37
+ })),
38
+ };
39
+ };
@@ -0,0 +1,35 @@
1
+ /**
2
+ * Copyright IBM Corp. 2021, 2026
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+ // descriptor factory for the detail resource every domain registers
6
+ import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+ import { toJsonResourceResponse } from "./responses.js";
8
+ import { decodeUriSegment } from "./uri.js";
9
+ const JSON_MIME_TYPE = "application/json";
10
+ export const defineDetailResource = ({ name, uriTemplate, title, description, variableName, complete, read, }) => {
11
+ return {
12
+ name,
13
+ template: new ResourceTemplate(uriTemplate, {
14
+ list: undefined,
15
+ complete: {
16
+ [variableName]: (value) => complete(value),
17
+ },
18
+ }),
19
+ config: {
20
+ title,
21
+ description,
22
+ mimeType: JSON_MIME_TYPE,
23
+ },
24
+ readCallback: async (uri, variables) => {
25
+ const variable = variables[variableName];
26
+ if (typeof variable !== "string" || variable.trim().length === 0) {
27
+ return toJsonResourceResponse(uri.toString(), {
28
+ found: false,
29
+ message: `Missing ${variableName} variable.`,
30
+ });
31
+ }
32
+ return read(decodeUriSegment(variable));
33
+ },
34
+ };
35
+ };
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Copyright IBM Corp. 2021, 2026
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+ export const ICONS_URI = "hds://icons";
6
+ export const ICON_BY_NAME_URI_TEMPLATE = `${ICONS_URI}/{iconName}`;
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Copyright IBM Corp. 2021, 2026
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+ import { completeFromAliases, withSafeCompletion, } from "../completions.js";
6
+ import { defineDetailResource } from "../define-resource.js";
7
+ import { toJsonResourceResponse } from "../responses.js";
8
+ import { buildDetailUri } from "../uri.js";
9
+ import { ICONS_URI, ICON_BY_NAME_URI_TEMPLATE } from "./constants.js";
10
+ import { getOrLoadIconStore } from "../../stores/hds-icons/index.js";
11
+ import { toSerializableIcon } from "./utils.js";
12
+ export const completeIconNames = (icons, value) => {
13
+ return completeFromAliases({
14
+ items: icons,
15
+ getAliases: (icon) => [icon.iconName, ...icon.fileNames],
16
+ getValue: (icon) => icon.iconName,
17
+ value,
18
+ });
19
+ };
20
+ export const readIconByNameResource = (store, iconName) => {
21
+ const icon = store.getIconByName(iconName);
22
+ const uri = buildDetailUri(ICONS_URI, iconName);
23
+ if (icon === null) {
24
+ return toJsonResourceResponse(uri, {
25
+ found: false,
26
+ requestedIconName: iconName,
27
+ message: "Icon not found for provided iconName or fileName.",
28
+ });
29
+ }
30
+ return toJsonResourceResponse(uri, {
31
+ found: true,
32
+ requestedIconName: iconName,
33
+ icon: toSerializableIcon(icon),
34
+ });
35
+ };
36
+ export const createGetIconByNameResource = (getStore) => {
37
+ return defineDetailResource({
38
+ name: "get_hds_icon",
39
+ uriTemplate: ICON_BY_NAME_URI_TEMPLATE,
40
+ title: "HDS icon detail",
41
+ description: "Detailed Flight icon record for a specific icon name",
42
+ variableName: "iconName",
43
+ complete: withSafeCompletion("get_hds_icon", (value) => completeIconNames(getStore().listIconAliases(), value)),
44
+ read: (iconName) => readIconByNameResource(getStore(), iconName),
45
+ });
46
+ };
47
+ const getIconByNameResource = createGetIconByNameResource(getOrLoadIconStore);
48
+ export default getIconByNameResource;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Copyright IBM Corp. 2021, 2026
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+ import { toJsonResourceResponse } from "../responses.js";
6
+ import { ICONS_URI } from "./constants.js";
7
+ import { getOrLoadIconStore } from "../../stores/hds-icons/index.js";
8
+ import { toSerializableIconSummary } from "./utils.js";
9
+ export const readIconsResource = (store) => {
10
+ const meta = store.getMeta();
11
+ const payload = {
12
+ totalIconCount: meta.totalIconCount,
13
+ totalAssetCount: meta.totalAssetCount,
14
+ categories: meta.categories,
15
+ source: meta.source,
16
+ icons: store.listIcons().map((icon) => toSerializableIconSummary(icon)),
17
+ };
18
+ return toJsonResourceResponse(ICONS_URI, payload);
19
+ };
20
+ export const createGetIconsResource = (getStore) => {
21
+ return {
22
+ name: "get_hds_icons",
23
+ uri: ICONS_URI,
24
+ config: {
25
+ title: "HDS icon catalog index",
26
+ description: "Canonical list of Flight icons with summary metadata",
27
+ mimeType: "application/json",
28
+ },
29
+ readCallback: async () => readIconsResource(getStore()),
30
+ };
31
+ };
32
+ export const getIconsResource = createGetIconsResource(getOrLoadIconStore);
33
+ export default getIconsResource;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Copyright IBM Corp. 2021, 2026
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+ import getIconByNameResource from "./get-icon-by-name.js";
6
+ import getIconsResource from "./get-icons.js";
7
+ const HDS_ICONS_RESOURCES = [
8
+ getIconsResource,
9
+ getIconByNameResource,
10
+ ];
11
+ export default HDS_ICONS_RESOURCES;
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Copyright IBM Corp. 2021, 2026
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+ export const toSerializableIconSummary = (icon) => {
6
+ return {
7
+ iconName: icon.iconName,
8
+ description: icon.description,
9
+ category: icon.category,
10
+ sizes: icon.sizes,
11
+ hasMapping: icon.hasMapping,
12
+ };
13
+ };
14
+ export const toSerializableIcon = (icon) => {
15
+ return {
16
+ ...toSerializableIconSummary(icon),
17
+ variants: icon.variants.map((variant) => ({
18
+ id: variant.id,
19
+ fileName: variant.fileName,
20
+ size: variant.size,
21
+ width: variant.width,
22
+ height: variant.height,
23
+ ...(variant.mapping === undefined ? {} : { mapping: variant.mapping }),
24
+ })),
25
+ };
26
+ };
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Copyright IBM Corp. 2021, 2026
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+ import { withSafeResourceHandler } from "./responses.js";
6
+ import hdsIconsResources from "./hds-icons/index.js";
7
+ import tokensResources from "./tokens/index.js";
8
+ import componentsResources from "./components/index.js";
9
+ const RESOURCES = [
10
+ ...componentsResources,
11
+ ...hdsIconsResources,
12
+ ...tokensResources,
13
+ ];
14
+ export function registerResources(server) {
15
+ for (const resource of RESOURCES) {
16
+ if ("uri" in resource) {
17
+ server.registerResource(resource.name, resource.uri, resource.config, withSafeResourceHandler(resource.name, resource.readCallback));
18
+ }
19
+ else if ("template" in resource) {
20
+ server.registerResource(resource.name, resource.template, resource.config, withSafeResourceHandler(resource.name, resource.readCallback));
21
+ }
22
+ }
23
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Copyright IBM Corp. 2021, 2026
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+ export const toJsonResourceResponse = (uri, payload) => {
6
+ return {
7
+ contents: [
8
+ {
9
+ uri,
10
+ mimeType: "application/json",
11
+ text: JSON.stringify(payload, null, 2),
12
+ },
13
+ ],
14
+ };
15
+ };
16
+ export const withSafeResourceHandler = (resourceName, handler) => {
17
+ return async (...args) => {
18
+ const [uri] = args;
19
+ try {
20
+ return await handler(...args);
21
+ }
22
+ catch (error) {
23
+ console.error(`Resource handler failed (${resourceName}):`, error);
24
+ const errorPayload = error instanceof Error
25
+ ? { name: error.name, message: error.message }
26
+ : { message: String(error) };
27
+ return toJsonResourceResponse(uri.toString(), {
28
+ ok: false,
29
+ resource: resourceName,
30
+ error: errorPayload,
31
+ });
32
+ }
33
+ };
34
+ };
@@ -0,0 +1,2 @@
1
+ export const TOKENS_URI = "hds://tokens";
2
+ export const TOKEN_BY_KEY_URI_TEMPLATE = `${TOKENS_URI}/{tokenKey}`;
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Copyright IBM Corp. 2021, 2026
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+ import { completeFromAliases, withSafeCompletion } from "../completions.js";
6
+ import { defineDetailResource } from "../define-resource.js";
7
+ import { getOrLoadTokenStore } from "../../stores/tokens/index.js";
8
+ import { toJsonResourceResponse } from "../responses.js";
9
+ import { buildDetailUri } from "../uri.js";
10
+ import { TOKENS_URI, TOKEN_BY_KEY_URI_TEMPLATE } from "./constants.js";
11
+ import { toSerializableTokenSummary } from "./utils.js";
12
+ export const completeTokenKeys = (tokens, value) => {
13
+ return completeFromAliases({
14
+ items: tokens,
15
+ getAliases: (token) => [token.key, token.name, token.path.join(".")],
16
+ getValue: (token) => token.key,
17
+ value,
18
+ });
19
+ };
20
+ export const readTokenByKeyResource = (store, tokenKey) => {
21
+ const token = store.getTokenByKey(tokenKey);
22
+ const uri = buildDetailUri(TOKENS_URI, tokenKey);
23
+ if (token === null) {
24
+ return toJsonResourceResponse(uri, {
25
+ found: false,
26
+ requestedTokenKey: tokenKey,
27
+ message: "Token not found for provided tokenKey.",
28
+ });
29
+ }
30
+ return toJsonResourceResponse(uri, {
31
+ found: true,
32
+ requestedTokenKey: tokenKey,
33
+ token: {
34
+ ...toSerializableTokenSummary(token),
35
+ ...(token.original === undefined ? {} : { original: token.original }),
36
+ },
37
+ });
38
+ };
39
+ export const createGetTokenByKeyResource = (getStore) => {
40
+ return defineDetailResource({
41
+ name: "get_hds_token",
42
+ uriTemplate: TOKEN_BY_KEY_URI_TEMPLATE,
43
+ title: "HDS token detail",
44
+ description: "Detailed token record for a specific token key",
45
+ variableName: "tokenKey",
46
+ complete: withSafeCompletion("get_hds_token", (value) => completeTokenKeys(getStore().listTokens(), value)),
47
+ read: (tokenKey) => readTokenByKeyResource(getStore(), tokenKey),
48
+ });
49
+ };
50
+ const getTokenByKeyResource = createGetTokenByKeyResource(getOrLoadTokenStore);
51
+ export default getTokenByKeyResource;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Copyright IBM Corp. 2021, 2026
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+ import { toJsonResourceResponse } from "../responses.js";
6
+ import { getOrLoadTokenStore } from "../../stores/tokens/index.js";
7
+ import { toSerializableTokenSummary } from "./utils.js";
8
+ import { TOKENS_URI } from "./constants.js";
9
+ export const readTokensResource = (store) => {
10
+ const meta = store.getMeta();
11
+ const payload = {
12
+ totalTokenCount: meta.totalTokenCount,
13
+ source: meta.source,
14
+ tokens: store
15
+ .listTokens()
16
+ .map((token) => toSerializableTokenSummary(token)),
17
+ };
18
+ return toJsonResourceResponse(TOKENS_URI, payload);
19
+ };
20
+ export const createGetTokensResource = (getStore) => {
21
+ return {
22
+ name: "get_hds_tokens",
23
+ uri: TOKENS_URI,
24
+ config: {
25
+ title: "HDS token catalog index",
26
+ description: "Canonical list of tokens with summary metadata",
27
+ mimeType: "application/json",
28
+ },
29
+ readCallback: async () => readTokensResource(getStore()),
30
+ };
31
+ };
32
+ export const getTokensResource = createGetTokensResource(getOrLoadTokenStore);
33
+ export default getTokensResource;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Copyright IBM Corp. 2021, 2026
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+ import getTokensResource from "./get-tokens.js";
6
+ import getTokenByKeyResource from "./get-token-by-key.js";
7
+ const TOKENS_RESOURCES = [
8
+ getTokensResource,
9
+ getTokenByKeyResource,
10
+ ];
11
+ export default TOKENS_RESOURCES;
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Copyright IBM Corp. 2021, 2026
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+ export const toSerializableTokenSummary = (token) => {
6
+ return {
7
+ key: token.key,
8
+ name: token.name,
9
+ type: token.type,
10
+ value: token.value,
11
+ cssVar: token.cssVar,
12
+ category: token.category,
13
+ path: token.path,
14
+ };
15
+ };
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Copyright IBM Corp. 2021, 2026
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+ export {};
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Copyright IBM Corp. 2021, 2026
3
+ * SPDX-License-Identifier: MPL-2.0
4
+ */
5
+ export const buildDetailUri = (baseUri, key) => {
6
+ return `${baseUri}/${encodeURIComponent(key)}`;
7
+ };
8
+ export const decodeUriSegment = (value) => {
9
+ try {
10
+ return decodeURIComponent(value);
11
+ }
12
+ catch {
13
+ return value;
14
+ }
15
+ };