diff --git a/.cursorignore b/.cursorignore deleted file mode 100644 index 6f9f00f..0000000 --- a/.cursorignore +++ /dev/null @@ -1 +0,0 @@ -# Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..75adebf --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,167 @@ +# Contributing +Interested in contributing to the project? Review the following guidelines and our [planned architecture](./docs/architecture.md) to make sure +your contribution is aligned with the project's goals. + +## Development + +### Environment setup + +#### Tools +- [Node.js](https://nodejs.org/en/download/package-manager) +- NPM (or equivalent package manager) +- Git configured with your GitHub account + +#### Project setup +- Fork and clone the repository +- Open your terminal, or equivalent, in the codebase context and run the following commands + ```bash + npm install + npm run build + npm test + npm run test:integration + npm start + ``` + All tests should pass, and the application should start successfully with confirmation messaging in the terminal. + +#### Development workflow +- Make changes to the codebase +- Run tests to verify your changes do not break existing functionality +- Commit your changes and push them to your fork +- Open a pull request + +### Using Git + +#### Workflow +Our process follows the standard GitHub fork and pull request workflow. + +- Fork the repository +- Create a branch for your changes +- Submit a pull request towards the main repository default branch + +##### Main repository branches +- The `main` branch currently represents both development and stable releases + +> In the future, a consideration for a `stable` branch may be made against an increase in contributions. +> - `main` would be the default branch for development and feature work rebased from `stable` after release. +> - `stable` would be a branch used for stable releases/hashes, reference links, and only updated with release commits. + +#### Pull requests + +Development pull requests (PRs) should be opened against the default branch. + +> If your pull request work contains any of the following warning signs +> - has no related issue +> - ignores existing code style +> - out of sync commits (not rebased against the default branch) +> - poorly structured commits and messages +> - any one commit relies on other commits to work (beyond "review requested updates") +> - dramatic file restructures that attempt complex behavior +> - missing, relaxed, or removed linting, typings, and tests +> - overly complex TypeScript generics or generally over-the-top typings +> - dramatic unit test snapshot updates +> - affects any file not directly associated with the issue being resolved +> - affects "many" files +> - contains or is a minor grammatical fix +> +> You will be asked to either: +> - open an issue instead of a PR +> - restructure your commits +> - break the work into multiple pull requests +> - close the PR (typically, a last resort) + +#### Pull request commits, messaging + +Your pull request should contain Git commit messaging that follows [conventional commit types](https://www.conventionalcommits.org/) +to provide consistent history and help generate [CHANGELOG.md](./CHANGELOG.md) updates. + +Commit messages follow two basic guidelines: +- No more than `65` characters for the first line. +- Commit message formats follow the structure: + ``` + (): (#PR_NUMBER) + ``` + Where: + - **Type**: The type of work the commit resolves (e.g., `feat`, `fix`, `chore`, `docs`, `refactor`, `test`). + - **Scope**: The optional area of code affected (directory, filename, or concept). + - **Description**: What the commit work encompasses. + - **#PR_NUMBER**: The pull request number. Typically added automatically during merge/squash operations. Including it manually is optional. It can help with traceability during review. + +> If your **pull request contains multiple commits**, they will be squashed into a single commit before merging and the messaging +> altered to reflect current guidelines. + +#### Pull request test failures +Before any review takes place, all tests should pass. You may be asked to update your pull request to resolve any failing tests +before a review. + +> If you are unsure why your tests are failing, you should [review testing documentation](#testing). + + +### Code style guidance and conventions +Basic code style guidelines are generally enforced by ESLint, but there are additional guidelines. + +#### File structure +- File names use lowerCamelCase and dot notation (e.g., `server.http.ts`, `server.logger.ts`). +- Directory structure is organized by function, with all relevant files maintained in the `src` directory. + +#### Functionality, testing +- Functions should attempt to maintain a single responsibility. +- Function annotations follow a minimal JSDoc style; descriptions are encouraged. +- Tests should focus on functionality. +- Tests should not be written for external packages. That is the responsibility of the external package, or it shouldn't be used. + +#### Typescript +- Typings within the project may be generally loose for initial development but should be refined over time. +- Typings exposed to consumers should always attempt to maintain consistency. +- Typings for tests are less of a focus than functionality checks. + +### Testing +Current testing is based on Jest. + +> A consideration for Vitest is being made for the near future after base functionality is complete. + +#### Unit tests + +Unit tests are located in the `__tests__` directory. + +#### E2E tests + +E2E tests are located in the root `./tests` directory. + +## AI agent + +### User Section + +Current agent interaction can be triggered with the chat command + +- **`review the repo guidelines`** - Your agent should attempt to scan common markdown files and the guidelines directory + +For detailed information on agent interaction, see [guidelines/README.md](./guidelines/README.md). + +#### Customizing developer experience + +As developers, we often have our own preferred workflows, and that includes working with AI agents. To that point, we've added agent guidance +to allow customization for your work environment through a tool-agnostic git-ignored directory `./.agent` in the root of the project. + +#### Noting AI Agent contributions + +Please reference [PatternFly's AI-assisted development guidelines](https://github.com/patternfly/.github/blob/main/CONTRIBUTING.md) for guidance on how to +acknowledge AI agent contributions. + +### Agent Only +Agents: This repository contains a hierarchical guideline system. Agents should review agent-only comment blocks. + + diff --git a/README.md b/README.md index a442ec2..02cc264 100644 --- a/README.md +++ b/README.md @@ -1,114 +1,37 @@ # PatternFly MCP Server -A Model Context Protocol (MCP) server that provides access to PatternFly React development rules and documentation, built with Node.js and TypeScript. +A Model Context Protocol (MCP) server that provides access to PatternFly rules and documentation, built with Node.js. -## What is MCP? +The PatternFly MCP server is a comprehensive library resource for PatternFly. +It is intended to be extensible to meet the needs of different teams and projects, from simple to complex, from design to development. +[Read more about our roadmap and how we've structured the server in our architecture docs](./docs/architecture.md). -The Model Context Protocol (MCP) is an open standard that enables AI assistants to securely access external data sources and tools. This server provides a standardized way to expose PatternFly documentation and development rules to MCP-compatible clients. +## Requirements +- [Node.js 20+](https://nodejs.org/) +- NPM (or equivalent package manager) -## Prerequisites +## Quick Start -- Node.js 20.0.0 or higher - - **Note**: Loading **Tool Plugins** from an external file or package requires Node.js >= 22 at runtime. On Node < 22, the server starts with built‑in tools only and logs a one‑time warning. -- NPM (or another Node package manager) +The PatternFly MCP Server supports multiple configurations; see the [usage documentation](./docs/usage.md#mcp-client-configuration) for details. -## Installation +### For integrated use with an IDE -### Local development +#### Set a basic MCP configuration -1) Install dependencies: - -```bash -npm install -``` - -2) Build the project: - -```bash -npm run build -``` - -3) Run in watch/dev mode (TypeScript via tsx): - -```bash -npm run start:dev -``` - -### Use via npx (after publishing) - -```bash -npx @patternfly/patternfly-mcp -``` - -Or install locally in a project and run: - -```bash -npm install @patternfly/patternfly-mcp -npx @patternfly/patternfly-mcp -``` - -## Usage - -The MCP server tools are focused on being a resource library for PatternFly. Server tools are extensible by design and intended to be used in conjunction with the available MCP resources. - -### Built-in Tools - -#### Tool: searchPatternFlyDocs -Use this to search for PatternFly documentation URLs and component names. Accepts partial string matches or `*` to list all available components. From the content, you can select specific URLs and component names to use with `usePatternFlyDocs` - -- **Parameters**: `searchQuery`: `string` (required) - -#### Tool: usePatternFlyDocs -Fetch full documentation and component JSON schemas for specific PatternFly URLs or component names. - -> **Feature**: This tool automatically detects if a URL belongs to a component (or if a "name" is provided) and appends its machine-readable JSON schema (props, types, validation) to the response, providing a fused context of human-readable docs and technical specs. - -- **Parameters**: _Parameters are mutually exclusive. Provide either `name` OR `urlList` not both._ - - `name`: `string` (optional) - The name of the PatternFly component (e.g., "Button", "Modal"). **Recommended** for known component lookups. - - `urlList`: `string[]` (optional) - A list of specific documentation URLs discovered via `searchPatternFlyDocs`. - -#### Removed: ~~Tool: fetchDocs~~ -> "fetchDocs" has been integrated into "usePatternFlyDocs." - -~~Use this to fetch one or more specific documentation pages (e.g., concrete design guidelines or accessibility pages) after you’ve identified them via `usePatternFlyDocs`.~~ - -- ~~**Parameters**: `urlList`: `string[]` (required)~~ - -#### Deprecated: ~~Tool: componentSchemas~~ -> "componentSchemas" has been integrated into "usePatternFlyDocs." - -~~Use this to fetch the JSON Schema for a specific PatternFly component.~~ - -- ~~**Parameters**: `componentName`: `string` (required)~~ - -### Built-in Resources - -The server exposes a resource-centric architecture via the `patternfly://` URI scheme: - -- **`patternfly://context`**: General PatternFly development context and high-level rules. -- **`patternfly://docs/index`**: Index of all available documentation pages. -- **`patternfly://docs/{name}`**: Documentation for a specific component (e.g., `patternfly://docs/Button`). -- **`patternfly://schemas/index`**: Index of all available component schemas. -- **`patternfly://schemas/{name}`**: JSON Schema for a specific component (e.g., `patternfly://schemas/Button`). - -### MCP Client Configuration - -Most MCP clients use a JSON configuration to specify how to start this server. Below are examples you can adapt to your MCP client. - -#### Minimal client config (npx) +Minimal configuration ```json { "mcpServers": { "patternfly-docs": { "command": "npx", "args": ["-y", "@patternfly/patternfly-mcp@latest"], - "description": "PatternFly React development rules and documentation" + "description": "PatternFly rules and documentation" } } } ``` -#### HTTP transport mode +HTTP transport mode ```json { "mcpServers": { @@ -121,194 +44,39 @@ Most MCP clients use a JSON configuration to specify how to start this server. B } ``` -#### Custom local tool - -```json -{ - "mcpServers": { - "patternfly-docs": { - "command": "npx", - "args": [ - "-y", - "@patternfly/patternfly-mcp@latest", - "--tool", - "./mcp-tools/local-custom-tool.js" - ], - "description": "PatternFly MCP with a local custom tool" - } - } -} -``` - -## Features - -### HTTP Transport +[See the MCP Server Configuration documentation for more examples.](./docs/usage.md#mcp-client-configuration) -By default, the server uses stdio. Use the `--http` flag to enable HTTP transport. +### For development, advanced usage -#### Options -- `--http`: Enable HTTP transport mode. -- `--port `: Port to listen on (default: 8080). -- `--host `: Host to bind to (default: 127.0.0.1). -- `--allowed-origins `: Comma-separated list of allowed CORS origins. -- `--allowed-hosts `: Comma-separated list of allowed host headers. +#### Run the server directly +Run the server immediately via `npx`: -#### DNS Rebinding Protection -This server enables DNS rebinding protection by default when running in HTTP mode. If you're behind a proxy or load balancer, ensure the client sends a correct `Host` header and configure `--allowed-hosts` accordingly. - -### Logging - -The server uses a `diagnostics_channel`–based logger that keeps STDIO stdout pure by default. No terminal output occurs unless you enable a sink. - -- `--log-stderr`: Enable terminal logging. -- `--log-protocol`: Forward logs to MCP clients (requires advertising `capabilities.logging`). -- `--log-level `: Set log level (`debug`, `info`, `warn`, `error`). Default: `info`. -- `--verbose`: Shortcut for `debug` level. - -Example: ```bash -npx @patternfly/patternfly-mcp --log-stderr --log-level debug +npx -y @patternfly/patternfly-mcp ``` -### Disabled: ~~Docs-host mode (local llms.txt mode)~~ - -> Docs-host mode will be removed or replaced in a future release. -> -> Docs-host mode was intended to be a more efficient way for accessing text file versions of PatternFly documentation and link -> resources. That effort was intended to help load times and token counts while attempting to account for future API work. -> Docs-host mode documentation and links have experienced drift with recent updates to PatternFly resources. That drift combined -> with the introduction of MCP server resources concludes in disabling Docs-host mode while we evaluate its removal or replacement. -> -> If you have been using Docs-host mode, there's a probability you've been leveraging model inference instead of PatternFly -> documentation. You can continue passing the `--docs-host` flag, it will not break the CLI, but it will no-longer affect how the -> PatternFly MCP server loads documentation and link resources. - -~~If you run the server with `--docs-host`, local paths you pass in `urlList` are resolved relative to the `llms-files` folder at the repository root. This is useful when you have pre-curated `llms.txt` files locally.~~ +Or with options -- `--docs-host`: Running this flag produces no results. ~~Local paths you pass in `urlList` are resolved relative to the `llms-files` folder.~~ - -Example: ```bash -npx @patternfly/patternfly-mcp --docs-host +npx -y @patternfly/patternfly-mcp --log-stderr --verbose ``` -~~Then, passing a local path such as `react-core/6.0.0/llms.txt` in `urlList` will load from `llms-files/react-core/6.0.0/llms.txt`.~~ - -### MCP Tool Plugins - -You can extend the server's capabilities by loading **Tool Plugins** at startup. These plugins run out‑of‑process in an isolated **Tools Host** (Node.js >= 22) to ensure security and stability. - -#### CLI Usage -- `--tool `: Load one or more plugins. You can provide a path to a local file or the name of an installed NPM package. - - *Examples*: `--tool @acme/my-plugin`, `--tool ./local-plugins/weather-tool.js`, `--tool ./a.js,./b.js` -- `--plugin-isolation `: Tools Host permission preset. - - **Default**: `strict`. In strict mode, network and filesystem write access are denied; fs reads are allow‑listed to your project and resolved plugin directories. - -#### Behavior and Limitations -- **Node version gate**: Node < 22 skips loading plugins from external sources with a warning; built‑ins still register. -- **Supported inputs**: ESM packages (installed in `node_modules`) and local ESM files with default exports. -- **Not supported**: Raw TypeScript sources (`.ts`) or remote `http(s):` URLs. - -#### Troubleshooting -- If tool plugins don't appear, verify the Node version and check logs (enable `--log-stderr`). -- Startup `load:ack` warnings/errors from tool plugins are logged when stderr/protocol logging is enabled. -- If `tools/call` rejects with schema errors, ensure `inputSchema` is valid. See [Authoring Tools](#authoring-tools) for details. -- If the tool is having network access issues, you may need to configure `--plugin-isolation none`. This is generally discouraged for security reasons but may be necessary in some cases. +#### Inspect the server +Visualize and test the MCP interface: -#### Terminology -- **`Tool`**: The low-level tuple format `[name, schema, handler]`. -- **`Tool Config`**: The authoring object format `{ name, description, inputSchema, handler }`. -- **`Tool Factory`**: A function wrapper `(options) => Tool` (internal). -- **`Tool Module`**: The programmatic result of `createMcpTool`, representing a collection of tools. - -#### Authoring Tools - -We recommend using the `createMcpTool` helper to define tools. It ensures your tools are properly normalized for the server. - -##### Authoring a single Tool Module -```ts -import { createMcpTool } from '@patternfly/patternfly-mcp'; - -export default createMcpTool({ - name: 'hello', - description: 'Say hello', - inputSchema: { - type: 'object', - properties: { name: { type: 'string' } }, - required: ['name'] - }, - async handler({ name }) { - return `Hello, ${name}!`; - } -}); -``` - -##### Authoring multiple tools in one module -```ts -import { createMcpTool } from '@patternfly/patternfly-mcp'; - -export default createMcpTool([ - { name: 'hi', description: 'Greets', inputSchema: {}, handler: () => 'hi' }, - { name: 'bye', description: 'Farewell', inputSchema: {}, handler: () => 'bye' } -]); -``` - -##### Input Schema Format -The `inputSchema` property accepts either **plain JSON Schema objects** or **Zod schemas**. Both formats are automatically converted to the format required by the MCP SDK. - -**JSON Schema (recommended):** -```ts -inputSchema: { - type: 'object', - properties: { - name: { type: 'string' }, - age: { type: 'number' } - }, - required: ['name'] -} -``` - -**Zod Schema:** -```ts -import { z } from 'zod'; - -inputSchema: { - name: z.string(), - age: z.number().optional() -} +```bash +npx -y @modelcontextprotocol/inspector npx @patternfly/patternfly-mcp ``` -### Embedding the Server - -You can embed the MCP server inside your application using the `start()` function and provide **Tool Modules** directly. -```ts -import { start, createMcpTool, type PfMcpInstance, type ToolModule } from '@patternfly/patternfly-mcp'; +#### Embed the server in your application -const echoTool: ToolModule = createMcpTool({ - name: 'echoAMessage', - description: 'Echo back the provided user message.', - inputSchema: { - type: 'object', - properties: { message: { type: 'string' } }, - required: ['message'] - }, - handler: async (args: { message: string }) => ({ text: `You said: ${args.message}` }) -}); +```typescript +import { start } from '@patternfly/patternfly-mcp'; +// Remember to avoid using console.log and info, they pollute STDOUT async function main() { - const server: PfMcpInstance = await start({ - toolModules: [ - echoTool - ] - }); - - // Optional: observe refined server logs in‑process - server.onLog((event) => { - if (event.level !== 'debug') { - console.warn(`[${event.level}] ${event.msg || ''}`); - } - }); + const server = await start(); // Graceful shutdown process.on('SIGINT', async () => { @@ -320,32 +88,18 @@ async function main() { main(); ``` -## Development and Maintenance +[See the development documentation for additional examples, CLI and embedded server options.](./docs/development.md) -### Scripts -- `npm run build`: Build the project (cleans dist, type-checks, bundles). -- `npm test`: Run unit tests. -- `npm run test:integration`: Run e2e tests. -- `npm run start:dev`: Run with `tsx` in watch mode. -- `npm run test:lint`: Run ESLint. +## Documentation -### Environment Variables -- `DOC_MCP_FETCH_TIMEOUT_MS`: Milliseconds to wait before aborting an HTTP fetch (default: 15000). +For comprehensive usage, development, and project state [read the docs](./docs/README.md). -### Inspector-CLI Examples -```bash -npx @modelcontextprotocol/inspector-cli \ - --config ./mcp-config.json \ - --server patternfly-docs \ - --cli \ - --method tools/call \ - --tool-name usePatternFlyDocs \ - --tool-arg urlList='["documentation/guidelines/README.md"]' -``` +## Contributing + +Contributing? Guidelines can be found here [CONTRIBUTING.md](./CONTRIBUTING.md). + +### AI agent + +If you're using an AI assistant to help with development in this repository, please prompt it to `review the repo guidelines` to ensure adherence to project conventions. -## Resources -- [Model Context Protocol](https://modelcontextprotocol.io/) -- [PatternFly React](https://www.patternfly.org/) -- [MCP TypeScript SDK](https://github.com/modelcontextprotocol/typescript-sdk) -- [Node.js Documentation](https://nodejs.org/en/docs/) -- [TypeScript Documentation](https://www.typescriptlang.org/docs/) +Guidelines for developer-agent interaction can be found in [CONTRIBUTING.md](./CONTRIBUTING.md#ai-agent). diff --git a/cspell.config.json b/cspell.config.json new file mode 100644 index 0000000..1a1df4e --- /dev/null +++ b/cspell.config.json @@ -0,0 +1,19 @@ +{ + "language": "en", + "words": [ + "amet", + "ized", + "llms", + "midrun", + "modelcontextprotocol", + "nosniff", + "onsessioninitialized", + "onsessionclosed", + "patternfly", + "rereview", + "sparkline", + "streamable", + "unrepresentable", + "unsub" + ] +} diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..4fe6fb1 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,16 @@ +# PatternFly MCP Server Documentation + +Welcome to the PatternFly MCP Server documentation. This guide is organized by user intent. + +### 🚀 Usage +- **[MCP Tools and Resources](./usage.md)**: Use built-in tools and resources like `searchPatternFlyDocs` and `usePatternFlyDocs`. +- **[Client Configuration](./usage.md)**: Configure the server for your environment. + +### 🛠️ Developer Reference +- **[CLI Reference](./development.md#cli-usage)**: Reference of server options. +- **[API Reference](./development.md#programmatic-usage)**: Using the server as a base library in your own Node.js MCP. +- **[Examples](./examples/README.md)**: Standalone snippets for HTTP transport, embedding, and custom tools. + +### 🏗️ Architecture & Design +- **[System Architecture](./architecture.md)**: Design concept and architecture. +- **[Roadmap](./architecture.md#roadmap)**: Future plans for the project. diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..adc3a03 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,107 @@ +# Architecture & roadmap + +## Design and core concepts + +The PatternFly MCP server is centered around the concept of a library for all things PatternFly. It is intended to be extensible to meet the needs of different teams and projects, from simple to complex, from design to development. + +### The library, PatternFly integration + +PatternFly integration is centered around the following current, and future, tools: +- Searching for a resource +- Use and read a resource +- Finding, or discovering, a resource + +#### Search PatternFly documentation + +A built-in tool for searching PatternFly documentation and resources integrated into the server. + +#### Use PatternFly documentation + +A built-in tool for reading and using PatternFly documentation and resources integrated into the server. + +#### Find and discover PatternFly documentation + +An evolving "future" tool (still undergoing refinement) for finding PatternFly documentation and resources not directly integrated into the server. + +> This tool treats the MCP server as a library. Like a library, sometimes you need an interlibrary loan to gain access to the resource you need. +> +> The interlibrary concept is key because it starts to highlight that this third MCP tool could +> - Help provide updates for all PatternFly MCP server built-in tools, resources, and prompts +> - Maintain up-to-date documentation and resources +> - Provide a tailored experience for users based on their use patterns (e.g., a designer's experience is tailored to design, a developer's experience is tailored to development) + +### Tools, resources, and prompts as customizable plugins + +Tools, resources, and prompts as customizable plugins are the result of predictable MCP SDK patterns. In the case of the PatternFly MCP server, +this actively plays a role in the library architecture because it allows us to focus on providing stability. + +Key goals aided by moving towards plugins: +- **Providing a tailored experience for users** - Plugins a designer uses may differ from those of a developer, researcher, or community member. +- **Evolving/future proofing** - Plugins can evolve over time, and the MCP server can evolve to support them. (e.g., a new JS framework or design framework, etc.) +- **Maintainability** - MCP server core can focus on features and issues while plugins are added and maintained by the community. + +## Server architecture + +### Current state + +```mermaid +flowchart TD + subgraph A1["MCP server"] + subgraph E1["Session context"] + subgraph F1["Logging, stats context"] + subgraph F1A["Built-In tools"] + F1AA(["Search PF docs"]) + F1AB(["Use PF docs"]) + end + F1B --> F1A + F1B(["Built-In resources"]) + end + end + D1(["Server proxy"]) + end + B1(["Local external tools, prompts, resources"]) + B1 <--> D1 +``` + +## Roadmap + +### Planned features and integrations + +To get towards our future state, there are a series of planned features and integrations. + +Current focus: +- **YAML configuration for remote tools, resources and prompts** - YAML configuration for remote MCP tools, resources, and prompt plugins +- **MCP resource, prompts, and helper function sharing** - A way to share MCP resources, prompts, and helper functions towards external tool plugins. +- **Find PatternFly documentation tool** - A tool that reaches out to known PatternFly documentation sources, caches locally, and integrates the results with existing MCP tools and resources. +- **PatternFly API integration** - A JSON API for PatternFly documentation, components, and patterns. +- **Hosted resource for sharing MCP tools, resources, prompts** - Shared tooling customization through PatternFly AI tooling repository (or equivalent) + +Under consideration: +- **MCP client** - A tailored MCP client specific for the PatternFly MCP server. +- **Auditing for shared tools, resources, and prompts** - An auditing tool that helps you refine your shared tools, resources, and prompts. +- **Containerized PatternFly MCP server, client, and LLM** - A containerized PatternFly MCP server, client, and embedded LLM. Use your own PatternFly chat client resource. + +#### Future state + +```mermaid +flowchart TD + subgraph A1["MCP server"] + subgraph E1["Session context"] + subgraph F1["Logging, stats context"] + F1C(["Built-In prompts"]) + F1C <--> F1A + subgraph F1A["Built-In tools"] + F1AA(["Search PF docs"]) + F1AB(["Use PF docs"]) + F1AC(["Find PF docs"]) + end + F1B <--> F1A + F1B(["Built-In resources"]) + end + end + F1B --> D1 + D1(["Server proxy"]) + end + B1(["Local and remote external tools, prompts, resources"]) + B1 <--> D1 +``` diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 0000000..8ab75a0 --- /dev/null +++ b/docs/development.md @@ -0,0 +1,356 @@ +# Development usage + +Complete guide to using the PatternFly MCP Server for development including CLI and programmatic API usage. + +**Development:** +- [CLI Usage](#cli-usage) +- [Programmatic Usage](#programmatic-usage) +- [Tool Plugins](#tool-plugins) +- [Initial Troubleshooting](#initial-troubleshooting) + +## CLI Usage + +### Available options + +| Flag | Description | Default | +|:--------------------------------------|:------------------------------------------------------|:---------------------| +| `--http` | Enable HTTP transport mode | `false` (stdio mode) | +| `--port ` | Port for HTTP transport | `8080` | +| `--host ` | Host to bind to | `127.0.0.1` | +| `--allowed-origins ` | Comma-separated list of allowed CORS origins | `none` | +| `--allowed-hosts ` | Comma-separated list of allowed host headers | `none` | +| `--tool ` | Path to external Tool Plugin (repeatable) | `none` | +| `--plugin-isolation ` | Isolation preset for external tools-as-plugins | `strict` | +| `--log-stderr` | Enable terminal logging | `false` | +| `--log-protocol` | Forward logs to MCP clients | `false` | +| `--log-level ` | Set log level (`debug`, `info`, `warn`, `error`) | `info` | +| `--verbose` | Shortcut for `--log-level debug` | `false` | +| `--docs-host` | **Disabled**, continued use will not break the server | `false` | + +#### Notes +- **Docs-host mode** - Docs-host mode has been disabled and will be removed in a future release. Its original purpose has been superseded by the move to MCP server resources. +- **HTTP transport mode** - By default, the server uses `stdio`. Use the `--http` flag to enable HTTP transport. +- **Logging** - The server uses a `diagnostics_channel`-based logger that keeps STDIO stdout pure by default. +- **Programmatic API** - The server can also be used programmatically with options. See [Programmatic Usage](#programmatic-usage) for more details. +- **Tool Plugins** - The server can load external tool plugins at startup. See [Tool Plugins](#tool-plugins) for more details. + +### Basic use scenarios + +**stdio mode (default):** +```bash +npx @patternfly/patternfly-mcp +``` + +**HTTP mode:** +```bash +npx @patternfly/patternfly-mcp --http --port 8080 +``` + +**HTTP mode with custom security**: +```bash +npx @patternfly/patternfly-mcp --http --port 3000 --allowed-origins "https://app.com" +``` + +**Loading external tool plugins**: +```bash +npx @patternfly/patternfly-mcp --tool ./first-tool.js --tool ./second-tool.ts +``` + +### Testing with MCP Inspector + +The `@modelcontextprotocol/inspector` is the recommended way to visualize the server's interface. + +1. **Start the Inspector**: + ```bash + npx -y @modelcontextprotocol/inspector npx @patternfly/patternfly-mcp + ``` +2. **Interact**: The inspector opens a web interface (typically at `http://localhost:5173`) where you can list tools, execute them, and view resource contents. + +**Example with repository context:** +```bash +npx @modelcontextprotocol/inspector-cli \ + --config ./mcp-config.json \ + --server patternfly-docs \ + --cli \ + --method tools/call \ + --tool-name usePatternFlyDocs \ + --tool-arg urlList='["documentation/guidelines/README.md"]' +``` + +## Programmatic usage + +### Server configuration options + +The `start()` function accepts an optional `PfMcpOptions` object for programmatic configuration. Use these options to customize behavior, transport, and logging for embedded instances. + +| Option | Type | Description | Default | +|:----------------------|:-----------------------------------------|:----------------------------------------------------------------------|:-------------------| +| `toolModules` | `ToolModule \| ToolModule[]` | Array of tool modules or paths to external tool plugins to be loaded. | `[]` | +| `isHttp` | `boolean` | Enable HTTP transport mode. | `false` | +| `http.port` | `number` | Port for HTTP transport. | `8080` | +| `http.host` | `string` | Host to bind to. | `127.0.0.1` | +| `http.allowedOrigins` | `string[]` | List of allowed CORS origins. | `[]` | +| `http.allowedHosts` | `string[]` | List of allowed host headers. | `[]` | +| `pluginIsolation` | `'none' \| 'strict'` | Isolation preset for external tools-as-plugins. | `'strict'` | +| `logging.level` | `'debug' \| 'info' \| 'warn' \| 'error'` | Set the logging level. | `'info'` | +| `logging.stderr` | `boolean` | Enable terminal logging to stderr. | `false` | +| `logging.protocol` | `boolean` | Forward logs to MCP clients. | `false` | +| `mode` | `'cli' \| 'programmatic' \| 'test'` | Specifies the operation mode. | `'programmatic'` | +| `docsPath` | `string` | Path to the documentation directory. | (Internal default) | + +#### Example usage + +```typescript +import { start, type PfMcpInstance, type PfMcpOptions } from '@patternfly/patternfly-mcp'; + +const options: PfMcpOptions = { + isHttp: true, + http: { + port: 3000, + allowedOrigins: ['https://your-app.com'] + }, + logging: { + level: 'debug', + stderr: true + } +}; + +const server: PfMcpInstance = await start(options); +``` + +### Server instance + +The server instance exposes the following methods: + +- `isRunning()` - Returns a `boolean`, `true` if the server is running. +- `onLog(callback)` - Registers a callback for server logs, returns a `PfMcpLogEvent` object. +- `stop()` - Stops the server. +- `getStats()` - Returns a `PfMcpStats` object containing server metrics and diagnostic channel IDs. + +#### server.isRunning() + +Checks if the server is running, returns a `boolean`, `true` if the server is running. + +```typescript +import { start, type PfMcpInstance } from '@patternfly/patternfly-mcp'; + +const server: PfMcpInstance = await start({ /* options */ }); + +if (server.isRunning()) { + // Server is running +} +``` + +#### server.onLog(callback) + +Registers a callback for server logs. The callback receives a `PfMcpLogEvent` object. + +```typescript +import { start, type PfMcpInstance, type PfMcpLogEvent } from '@patternfly/patternfly-mcp'; + +const server: PfMcpInstance = await start({ /* options */ }); + +server.onLog((event: PfMcpLogEvent) => { + if (event.level !== 'debug') { + console.warn(`[${event.level}] ${event.msg || ''}`); + } +}); +``` + +#### server.stop() + +Stops the server. + +```typescript +import { start, type PfMcpInstance } from '@patternfly/patternfly-mcp'; + +const server: PfMcpInstance = await start({ /* options */ }); + +process.on('SIGINT', async () => { + await server.stop(); + process.exit(0); +}); +``` + +#### server.getStats() + +Returns a `PfMcpStats` object containing server metrics, includes diagnostic channel IDs for listening to server activity. + +```typescript +import { channel, unsubscribe, subscribe } from 'node:diagnostics_channel'; +import { start, type PfMcpInstance, type PfMcpStats, type PfMcpStatReport } from '@patternfly/patternfly-mcp'; + +const server: PfMcpInstance = await start({ /* options */ }); +const stats: PfMcpStats = server.getStats(); + +const logChannel = channel(stats.health.channelId); +const logHandler = (event: PfMcpStatReport) => { + /* Handle log events, on MCP server close unsubscribe */ +}; + +const logSubscription = subscribe(logChannel, logHandler); + +``` + +### Typing reference + +Reference typings are exported from the package. The full listing can be found in [src/index.ts](../src/index.ts). + +### Embedding the Server + +You can embed the MCP server inside your application using the `start()` function and provide **Tool Modules** directly. + +```ts +import { start, createMcpTool, type PfMcpInstance, type ToolModule } from '@patternfly/patternfly-mcp'; + +const echoTool: ToolModule = createMcpTool({ + name: 'echoAMessage', + description: 'Echo back the provided user message.', + inputSchema: { + type: 'object', + properties: { message: { type: 'string' } }, + required: ['message'] + }, + handler: async (args: { message: string }) => ({ + content: [{ type: 'text', text: `You said: ${args.message}` }] + }) +}); + +const main = async () => { + const server: PfMcpInstance = await start({ + toolModules: [ + echoTool + ] + }); + + // Optional: observe refined server logs in‑process. + // We recommend getting in the habit of avoiding use of console.log and info, they pollute STDOUT. + server.onLog((event) => { + if (event.level !== 'debug') { + console.warn(`[${event.level}] ${event.msg || ''}`); + } + }); + + // Graceful shutdown + process.on('SIGINT', async () => { + await server.stop(); + process.exit(0); + }); +} + +main(); +``` + +See [examples/](examples/) for more programmatic usage examples. + +## MCP Tool Plugins + +You can extend the server's capabilities by loading **Tool Plugins** at startup. These plugins run out‑of‑process in an isolated **Tools Host** to ensure security and stability. + +### Environmental Requirements + +- **Node.js >= 22**: Loading external tool plugins (`--tool`) requires Node.js version 22 or higher due to the use of advanced process isolation and ESM module loading features. +- **ESM**: Plugins MUST be authored as ECMAScript Modules. +- **Dependency Resolution**: Plugins importing from `@patternfly/patternfly-mcp` require the package to be resolvable in the execution environment. This may require a local `npm install` in the plugin's directory or project root if the package is not available globally. + +### Security & Isolation + +The server provides two isolation modes for external plugins via the `--plugin-isolation` flag: + +- **`strict` (Default)**: The plugin runs in a restricted environment with limited access to the system. This is the recommended mode for most tools. +- **`none`**: The plugin has full access to the system environment, including the filesystem and network, inherited from the host process. Use this mode ONLY if your tool requires specific system access (e.g., executing Git commands or accessing local files outside the sandbox) and you trust the plugin code. + +> **Warning**: Disabling isolation (`--plugin-isolation none`) increases the security risk. Always document the reason for requiring this mode in your tool's documentation. + +### Terminology + +- **`Tool`**: The low-level tuple format `[name, schema, handler]`. +- **`Tool Config`**: The authoring object format `{ name, description, inputSchema, handler }`. +- **`Tool Factory`**: A function wrapper `(options) => Tool` (internal). +- **`Tool Module`**: The programmatic result of `createMcpTool`, representing a collection of tools. + +### Authoring Tools + +We recommend using the `createMcpTool` helper to define tools. It ensures your tools are properly normalized for the server. + +#### Authoring a single Tool Module + +```ts +import { createMcpTool } from '@patternfly/patternfly-mcp'; + +export default createMcpTool({ + name: 'hello', + description: 'Say hello', + inputSchema: { + type: 'object', + properties: { name: { type: 'string' } }, + required: ['name'] + }, + async handler({ name }) { + return { + content: [{ type: 'text', text: `Hello, ${name}!` }] + }; + } +}); +``` + +#### Authoring multiple tools in one module + +```ts +import { createMcpTool } from '@patternfly/patternfly-mcp'; + +export default createMcpTool([ + { name: 'hi', description: 'Greets', inputSchema: {}, handler: () => ({ content: [{ type: 'text', text: 'hi' }] }) }, + { name: 'bye', description: 'Farewell', inputSchema: {}, handler: () => ({ content: [{ type: 'text', text: 'bye' }] }) } +]); +``` + +#### Input Schema Format + +The `inputSchema` property accepts either **plain JSON Schema objects** or **Zod schemas**. Both formats are automatically converted to the format required by the MCP SDK. + +**JSON Schema (recommended):** +```ts +const inputSchema = { + type: 'object', + properties: { + name: { type: 'string' }, + age: { type: 'number' } + }, + required: ['name'] +}; +``` + +**Zod Schema:** +```ts +import { z } from 'zod'; + +const inputSchema = z.object({ + name: z.string(), + age: z.number().optional() +}); +``` + +See [examples/toolPluginHelloWorld.js](examples/toolPluginHelloWorld.js) for a basic example. + +## Initial Troubleshooting + +### Tool Plugins + +- **Plugins don't appear**: Verify the Node version (requires Node.js >= 20; >= 22 for tool plugins) and check logs (enable `--log-stderr`). +- **Startup warnings/errors**: Startup `load:ack` warnings/errors from tool plugins are logged when stderr/protocol logging is enabled. +- **Schema errors**: If `tools/call` rejects with schema errors, ensure `inputSchema` is valid. See [Authoring Tools](#authoring-tools) for details. +- **Network access issues**: If the tool is having network access issues, you may need to configure `--plugin-isolation none`. This is generally discouraged for security reasons but may be necessary in some cases. + +### HTTP Transport + +- **Connection issues**: Ensure the port is not already in use and the host is correct. +- **CORS errors**: Configure `--allowed-origins` if accessing from a web client. +- **DNS rebinding protection**: If behind a proxy, ensure correct `Host` header and configure `--allowed-hosts`. + +### General Issues + +- **Server won't start**: Check Node.js version (requires Node.js >= 20; >= 22 for tool plugins). +- **Missing tools/resources**: Verify the server started successfully and check logs with `--log-stderr`. +- **Type errors**: Ensure TypeScript types are installed: `npm install --save-dev @types/node` + diff --git a/docs/examples/README.md b/docs/examples/README.md new file mode 100644 index 0000000..f8802a3 --- /dev/null +++ b/docs/examples/README.md @@ -0,0 +1,25 @@ +# Code Examples + +PatternFly MCP code examples for tooling plugins and development use cases. + +- **[embeddedBasicStdio.js](embeddedBasicStdio.js)** - Embedding a basic STDIO transport configuration and usage +- **[embeddedBasicHttp.js](embeddedBasicHttp.js)** - Embedding a basic HTTP transport configuration and usage +- **[embeddedInlineTool.ts](embeddedInlineTool.ts)** - Embedding the server and using a custom inline tool to make your own MCP server +- **[toolPluginGitStatus.js](toolPluginGitStatus.js)** - A custom tool using Git +- **[toolPluginHelloWorld.js](toolPluginHelloWorld.js)** - A basic JS tool plugin example + + +## Adding new example guidance + +Examples should follow the basic guidelines: + +1. This index is updated with an example link +2. Filenames are lowerCamelCased +3. Keep examples short; this is an introduction to the project +4. Examples are either JS or TS with ESM import/exports +5. Comments/annotations are used to explain key concepts +6. Examples are linted from the project's linting configs with + - `npm run test:lint` + - `npm run test:types` + - `npm run test:spell-docs` +7. Examples are tested and can be run without errors diff --git a/docs/examples/embeddedBasicHttp.js b/docs/examples/embeddedBasicHttp.js new file mode 100644 index 0000000..f891116 --- /dev/null +++ b/docs/examples/embeddedBasicHttp.js @@ -0,0 +1,56 @@ +/** + * An embedded basic HTTP Transport example. + * + * This example demonstrates how to start the PatternFly MCP Server + * with HTTP transport and security configuration. + */ +import { start } from '@patternfly/patternfly-mcp'; + +const main = async () => { + console.warn('Embedding PatternFly MCP Server with HTTP transport...'); + + // Start server with HTTP transport + const server = await start({ + http: { + port: 8080, + host: '127.0.0.1', + allowedOrigins: ['https://app.example.com'], + allowedHosts: ['localhost', '127.0.0.1'] + }, + logging: { + stderr: true, + level: 'info' + } + }); + + console.warn('Server started on http://127.0.0.1:8080'); + console.warn(`Server is running: ${server.isRunning()}`); + + // Optional: Listen to server logs + server.onLog(event => { + console.warn(`[${event.level.toUpperCase()}] ${event.msg || ''}`); + }); + + // Get server statistics + const stats = await server.getStats(); + + console.warn('Server stats:', stats); + + // Graceful shutdown, Press Ctrl+C to stop. + process.on('SIGINT', async () => { + console.warn('\nShutting down server...'); + + await server.stop(); + + console.warn('Server stopped.'); + process.exit(0); + }); + + console.warn('Server is running. Press Ctrl+C to stop.'); + console.warn('Connect to: http://127.0.0.1:8080'); +}; + +main().catch(error => { + console.error('Failed to start server:', error); + process.exit(1); +}); diff --git a/docs/examples/embeddedBasicStdio.js b/docs/examples/embeddedBasicStdio.js new file mode 100644 index 0000000..46535ed --- /dev/null +++ b/docs/examples/embeddedBasicStdio.js @@ -0,0 +1,27 @@ +/** + * An embedded basic STDIO Transport example. + * + * This example demonstrates how to start the PatternFly MCP Server + * with default stdio transport. + */ +import { start } from '@patternfly/patternfly-mcp'; + +/** + * The main entry point for embedding a basic PatternFly MCP Server. + * + * - Avoid using console.log and info they pollute STDOUT. + * - Terminal logging can be enabled by setting `logging.stderr` to `true`, or you can use custom logging with the returned `server.onLog` callback. + * - Server instance returns `isRunning`, `getStats`, `onLog`, and `stop` methods. + */ +const main = async () => { + // Start the server with default (stdio) transport and terminal logging. + const server = await start({ logging: { stderr: true, level: 'info' } }); + + // Graceful shutdown, Press Ctrl+C to stop. + process.on('SIGINT', async () => { + await server.stop(); + process.exit(0); + }); +}; + +main().catch(console.error); diff --git a/docs/examples/embeddedInlineTool.ts b/docs/examples/embeddedInlineTool.ts new file mode 100644 index 0000000..4517e67 --- /dev/null +++ b/docs/examples/embeddedInlineTool.ts @@ -0,0 +1,90 @@ +/** + * Embedding inline tools example. + * + * This example demonstrates how to embed the PatternFly MCP Server + * inside your application with custom tools. + */ +// @ts-expect-error: Cannot find module '@patternfly/patternfly-mcp' - Remove this line if you're copying this example +import { start, createMcpTool, type PfMcpInstance, type PfMcpLogEvent, type PfMcpStats, type ToolModule } from '@patternfly/patternfly-mcp'; + +/** + * Echo tool - A custom tool that echoes back the provided user message. + * + * - Use "content" property with "TextContent" array for the response. + */ +const echoTool: ToolModule = createMcpTool({ + name: 'echoAMessage', + description: 'Echo back the provided user message.', + inputSchema: { + type: 'object', + properties: { + message: { + type: 'string', + description: 'The message to echo back' + } + }, + required: ['message'] + }, + handler: async (args: { message: string }) => ({ + content: [ + { + type: 'text', + text: `You said: ${args.message}` + } + ] + }) +}); + +/** + * Main entry point for embedding the PatternFly MCP Server. + * + * - Avoid using console.log and info they pollute STDOUT. + * - Terminal logging can be enabled by setting `logging.stderr` to `true`, or you can favor custom logging with the returned `server.onLog` callback. + * - Server instance returns `isRunning`, `getStats`, `onLog`, and `stop` methods. + */ +const main = async () => { + console.warn('Embedding PatternFly MCP Server...'); + + // Start a server with custom tools. + const server: PfMcpInstance = await start({ + toolModules: [ + echoTool + // Add additional inline tools here + ] + }); + + console.warn('Server embedded successfully!'); + console.warn(`Server is running: ${server.isRunning()}`); + + // Optional: Observe refined server logs in-process + server.onLog((event: PfMcpLogEvent) => { + if (event.level !== 'debug') { + console.warn(`[${event.level}] ${event.msg || ''}`); + } + }); + + // Get server statistics + const stats: PfMcpStats = await server.getStats(); + + console.warn('Server statistics:', stats); + + // Graceful shutdown, Press Ctrl+C to stop. + process.on('SIGINT', async () => { + console.warn('\nShutting down embedded server...'); + await server.stop(); + console.warn('Server stopped.'); + process.exit(0); + }); + + // Example: Your application logic here + console.warn('Your application is running with embedded MCP server.'); + console.warn('Press Ctrl+C to stop.'); + + // Keep the process alive + // In a real application, you would have your application logic here +}; + +main().catch(error => { + console.error('Failed to embed server:', error); + process.exit(1); +}); diff --git a/docs/examples/toolPluginGitStatus.js b/docs/examples/toolPluginGitStatus.js new file mode 100644 index 0000000..32c2b90 --- /dev/null +++ b/docs/examples/toolPluginGitStatus.js @@ -0,0 +1,95 @@ +/** + * Example of authoring a custom tool outside PatternFly that executes Git status commands. + * + * To load this tool into the PatternFly MCP server: + * 1. Save this file (e.g., `toolPluginGitStatus.js`) + * 2. Run the server with: `npx @patternfly/patternfly-mcp --tool /toolPluginGitStatus.js --plugin-isolation "none"` + * + * Note: + * - External tool file loading requires Node.js >= 22. + * - JS support only. TypeScript is only supported for embedding the server. + * - Requires ESM default export. + */ +import { spawn } from 'node:child_process'; +import { createMcpTool } from '@patternfly/patternfly-mcp'; + +/** + * Helper, execute a command using spawn with argument handling. + * + * @param command - Command to execute + * @param args - Command arguments + * @param options - Spawn options + */ +const spawnAsync = (command, args, options = {}) => new Promise((resolve, reject) => { + const child = spawn(command, args, { ...options, stdio: ['ignore', 'pipe', 'pipe'] }); + let stdout = ''; + let stderr = ''; + + child.stdout?.on('data', data => { + stdout += data.toString(); + }); + + child.stderr?.on('data', data => { + stderr += data.toString(); + }); + + child.on('close', code => { + const result = { stdout, stderr, code }; + + if (code === 0) { + resolve(result); + } else { + reject(Object.assign(new Error(stderr || stdout || `Exit code ${code}`), result)); + } + }); + + child.on('error', reject); +}); + +export default createMcpTool({ + name: 'getGitStatus', + description: 'Get Git repository status. Returns information about working directory, staged files, and recent commits.', + inputSchema: { + type: 'object', + properties: { + cwd: { + type: 'string', + description: 'Optional: Current working directory. Defaults to process.cwd().' + }, + short: { + type: 'boolean', + description: 'Optional: Use short format output (git status --short). Defaults to false.', + default: false + } + } + }, + async handler({ cwd, short = false }) { + try { + const args = short ? ['status', '--short'] : ['status']; + const { stdout, stderr } = await spawnAsync('git', args, { + cwd: cwd || process.cwd() + }); + + return { + content: [ + { + type: 'text', + text: stdout || stderr || 'No changes.' + } + ] + }; + } catch (error) { + const output = error.stdout || error.stderr || error.message; + + return { + content: [ + { + type: 'text', + text: output + } + ], + isError: true + }; + } + } +}); diff --git a/docs/examples/toolPluginHelloWorld.js b/docs/examples/toolPluginHelloWorld.js new file mode 100644 index 0000000..11a0b17 --- /dev/null +++ b/docs/examples/toolPluginHelloWorld.js @@ -0,0 +1,38 @@ +/** + * Example of authoring a custom tool. + * + * To load this tool into the PatternFly MCP server: + * 1. Save this file (e.g., `toolPluginHelloWorld.js`) + * 2. Run the server with: `npx @patternfly/patternfly-mcp --tool /toolPluginHelloWorld.js` + * + * Note: + * - External tool file loading requires Node.js >= 22. + * - JS support only. TypeScript is only supported for embedding the server. + * - Requires ESM default export. + */ +import { createMcpTool } from '@patternfly/patternfly-mcp'; + +export default createMcpTool({ + name: 'helloWorld', + description: 'A simple example tool that greets the user.', + inputSchema: { + type: 'object', + properties: { + name: { + type: 'string', + description: 'The name of the person to greet.' + } + }, + required: ['name'] + }, + async handler({ name }) { + return { + content: [ + { + type: 'text', + text: `Hello, ${name}! Welcome to the PatternFly MCP ecosystem.` + } + ] + }; + } +}); diff --git a/docs/usage.md b/docs/usage.md new file mode 100644 index 0000000..e757da5 --- /dev/null +++ b/docs/usage.md @@ -0,0 +1,154 @@ +# Usage guide + +A comprehensive guide to PatternFly MCP Server tools, resources, and configuration. + +**User Guide:** +- [Built-in Tools](#built-in-tools) +- [Built-in Resources](#built-in-resources) +- [MCP Client Configuration](#mcp-client-configuration) +- [Custom MCP Tool Plugins](#custom-mcp-tool-plugins) + +## Built-in tools + +> MCP tools represent the actions available to interact with the server. + +Core server tools provide a resource library for PatternFly. They are extensible by design and intended for use with available MCP resources. + +### Tool: searchPatternFlyDocs + +Use this to search for PatternFly documentation URLs and component names. Accepts partial string matches or `*` to list all available components. From the content, you can select specific URLs and component names to use with `usePatternFlyDocs`. + +**Parameters:** +- `searchQuery`: `string` (required) - Full or partial component name to search for (e.g., "button", "table", "*" for all components) + +**Example:** +```json +{ + "searchQuery": "button" +} +``` + +### Tool: usePatternFlyDocs + +Fetch full documentation and component JSON schemas for specific PatternFly URLs or component names. + +> **Feature**: This tool automatically detects if a URL belongs to a component (or if a "name" is provided) and appends its machine-readable JSON schema (props, types, validation) to the response, combining human-readable documentation with technical specifications. + +**Parameters:** _Parameters are mutually exclusive. Provide either `name` OR `urlList`, not both._ +- `name`: `string` (optional) - The name of the PatternFly component (e.g., "Button", "Modal"). **Recommended** for known component lookups. +- `urlList`: `string[]` (optional) - A list of specific documentation URLs discovered via `searchPatternFlyDocs` (max 15 at a time). + +**Example with name:** +```json +{ + "name": "Button" +} +``` + +**Example with urlList:** +```json +{ + "urlList": ["https://patternfly.org/components/button"] +} +``` + +### Deprecated tools + +#### ~~Tool: fetchDocs~~ (Removed) +> "fetchDocs" has been integrated into "usePatternFlyDocs." + +#### ~~Tool: componentSchemas~~ (Deprecated) +> "componentSchemas" has been integrated into "usePatternFlyDocs." + +## Built-in resources + +> MCP resources represent indexed collections of documentation. + +The server exposes this resource-centric architecture via the `patternfly://` URI scheme: + +- **`patternfly://context`**: General PatternFly MCP server context and high-level rules. +- **`patternfly://docs/index`**: Index of all available documentation pages. +- **`patternfly://docs/{name}`**: Documentation for a specific component (e.g., `patternfly://docs/Button`). +- **`patternfly://schemas/index`**: Index of all available component schemas. +- **`patternfly://schemas/{name}`**: JSON Schema for a specific component (e.g., `patternfly://schemas/Button`). + +## MCP client configuration + +Most MCP clients use JSON configuration to specify how the server is started. Below are examples you can adapt for your client. + +### Minimal client config (stdio) + +```json +{ + "mcpServers": { + "patternfly-docs": { + "command": "npx", + "args": ["-y", "@patternfly/patternfly-mcp@latest"], + "description": "PatternFly React development rules and documentation" + } + } +} +``` + +### HTTP transport mode + +```json +{ + "mcpServers": { + "patternfly-docs": { + "command": "npx", + "args": ["-y", "@patternfly/patternfly-mcp@latest", "--http", "--port", "8080"], + "description": "PatternFly docs (HTTP transport)" + } + } +} +``` + +### Custom local tool + +```json +{ + "mcpServers": { + "patternfly-docs": { + "command": "npx", + "args": [ + "-y", + "@patternfly/patternfly-mcp@latest", + "--tool", + "./mcp-tools/local-custom-tool.js" + ], + "description": "PatternFly MCP with a local custom tool" + } + } +} +``` + +### HTTP mode with security + +```json +{ + "mcpServers": { + "patternfly-docs": { + "command": "npx", + "args": [ + "-y", + "@patternfly/patternfly-mcp@latest", + "--http", + "--port", + "3000", + "--allowed-origins", + "https://app.com", + "--allowed-hosts", + "localhost,127.0.0.1" + ], + "description": "PatternFly docs (HTTP transport with security)" + } + } +} +``` + +## Custom MCP tool plugins + +You can extend the server's capabilities by loading custom **Tool Plugins** at startup. + +[See development documentation for tool plugins.](./development.md#mcp-tool-plugins) diff --git a/eslint.config.js b/eslint.config.js index 4bf7889..b77d361 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -123,5 +123,18 @@ export default [ // Allow console usage in tests (spies, debug) 'no-console': 0 } + }, + { + files: [ + 'docs/**/*.ts', + 'docs/**/*.js' + ], + rules: { + 'jsdoc/require-returns': 0, + 'jsdoc/require-returns-type': 0, + 'jsdoc/require-param-type': 0, + 'import/no-unresolved': 0, + 'n/no-process-exit': 0 + } } ]; diff --git a/guidelines/README.md b/guidelines/README.md new file mode 100644 index 0000000..33639d8 --- /dev/null +++ b/guidelines/README.md @@ -0,0 +1,50 @@ +# Agent Guidelines + +## Overview + +Agent-specific guidelines for the PatternFly MCP project, optimized for machine processing. + +## File Naming Convention + +- `agent_*`: Guidance for autonomous agents + +## Guidelines Index + +### Agent Guidelines + +- [Agent Behaviors](./agent_behaviors.md) - Comprehensive guide to agent behaviors, workflows, and standards +- [Agent Coding](./agent_coding.md) - Coding standards +- [Agent Testing](./agent_testing.md) - Testing procedures + +## User Guide + +### Available Trigger Phrases + +Agents should use these phrases as signals to consult specific documentation and source code: + +| Task / Intent | Reference Document | +|:------------------------------------|:-------------------------------------------------------------------------------------------------------------------------------------------------------| +| **"review the repo guidelines"** | Scan markdown files and guidelines directory. See [AI agent context](../CONTRIBUTING.md#ai-agent). | +| **"review app architecture"** | Review `docs/architecture.md` for current to future state, then review `src/*` for up-to-date specs. | +| **"review tool usage"** | Review `docs/usage.md` for built-in tools and configuration. | +| **"review development guide"** | Review `docs/development.md` for CLI, API, and plugin authoring. | +| **"create an example tool plugin"** | Review `guidelines/agent_coding.md`, `docs/development.md`, `docs/examples/*`, and `src/*` for context, coding standards, and existing example formats. | + +## Guidelines Processing Order + +1. **Guidelines Directory** (all files in the `guidelines/` directory) +2. **Local Guidelines** (`.agent/` directory) + +## Maintaining This Directory + +### File Maintenance Principles +- Update index files (e.g., `docs/examples/README.md` or `guidelines/README.md`) immediately when adding or removing content. +- Reference and index guidelines. Don't duplicate content +- Update references when adding new files +- Keep descriptions concise and focused + +### Adding New Guidelines +1. Add entry to "Guidelines Index" section +2. Include essential metadata +3. Provide brief description +4. Update processing order if needed diff --git a/guidelines/agent_behaviors.md b/guidelines/agent_behaviors.md new file mode 100644 index 0000000..9e238d0 --- /dev/null +++ b/guidelines/agent_behaviors.md @@ -0,0 +1,139 @@ +# Agent Behaviors + +## Overview + +Comprehensive guide to agent behaviors, workflows, and standards for the codebase. + +## For Agents + +### Processing Priority + +Critical - Process first when working with the repository. + +### Related Guidelines + +See the [Guidelines Index](./README.md#guidelines-index) for all guidelines. + +### Key Concepts + +- Repository context and structure +- Core behavior standards +- Trigger-based workflows +- Decision-making principles +- Guidance authoring standards + +## 1. Repository Context + +The PatternFly MCP server is a Model Context Protocol (MCP) server written in TypeScript. It provides access to PatternFly documentation, rules, and component schemas for LLMs. + +### Key Architectural Components + +For a detailed overview of the system design and roadmap, see [docs/architecture.md](../docs/architecture.md). + +- **Core Server**: Manages the MCP lifecycle and transport (stdio/http). +- **Tools**: Built-in actions for searching and using PatternFly documentation and technical specifications. +- **Resources**: URI-based access to PatternFly context, documentation, and schemas. +- **External Tools Host**: Isolated execution environment for custom tool plugins. +- **Caching & Performance**: Robust memoization and concurrency systems for efficient resource retrieval. + +## 2. Core Behavior Standards + +- **Sequential Processing**: Ask questions one at a time; process requests in logical order; complete one task before starting another. +- **Architectural Alignment**: Always confirm changes against the [system architecture and roadmap](../docs/architecture.md) before proceeding with implementation. +- **Reference-Based Implementation**: Review git history; study existing patterns (e.g., "creator" pattern for tools/resources); maintain code style consistency and follow [standard Git workflows](../CONTRIBUTING.md#using-git). +- **Commit Messaging Standards**: Follow the project's [commit messaging standards](../CONTRIBUTING.md#pull-request-commits-messaging). +- **Validation Required**: Follow checklists; verify requirements; test thoroughly. Review [pull request warning signs](../CONTRIBUTING.md#pull-requests) to avoid common pitfalls. +- **Confirmation Required**: Confirm success; summarize changes; explain impact; verify understanding. +- **Guidance Review Scope**: Unless the user explicitly asks, do not make recommendations on improving guidance if all you're asked to do is review guidance. +- **Environment Awareness**: + - Server execution requires **Node.js >= 20**. + - External tool plugins (`--tool`) require **Node.js >= 22** primarily for its robust **Permission Model** (`--experimental-permission`), which enables strict filesystem and network isolation. + - Always verify environment compatibility when proposing tools using modern Node.js features. +- **Security Context**: + - Default to `--plugin-isolation strict`. + - If a tool requires filesystem or network access beyond the sandbox, document the need for `--plugin-isolation none` explicitly. + - Warn users when a proposed solution requires disabling isolation. +- **State Management**: Use `.agent/` directory for local guidance and state; maintain context; preserve session information. +- **Security Awareness**: Be mindful of path traversal and isolation levels when working with external tools and resource loading. + +## 3. Trigger-Based Workflows + +### Trigger: "Research MCP SDK methods or issues" + +1. **Analyze** + - Confirm the installed MCP SDK version + - Research the error + - Identify conflict scenarios with code + - Identify potential test cases + +2. **Test** + - Run typing, lint, unit and e2e tests + - Confirm conflicts + - Test resolution options + +3. **Resolve** + - Adjust codebase + - Change code or confirm a solution + - Implement resolution + +4. **Validate** + - Test conflict resolution + - Confirm approach + +## 4. Decision-Making Guidelines + +1. **Consistency vs. Improvement** + - Favor consistency for minor changes + - Favor improvement for bugs and features + - Balance both when possible + +2. **Strictness vs. Flexibility** + - Strict for quality/security + - Flexible for style preferences + - Consider developer experience + +3. **Backward Compatibility** + - Minimize breaking changes + - Document when necessary + +4. **Architectural Alignment** + - Always verify that the proposed solution fits within the project's [long-term architecture and roadmap](../docs/architecture.md). + - Consult the roadmap before introducing major features or structural changes. + +## 5. Validation Procedures + +For all workflows: + +1. **Testing**: Run appropriate tests, ensure they pass, update snapshots only when intentional +2. **Documentation**: Verify accuracy, consistency, and helpful examples +3. **Code Quality**: Follow patterns, check edge cases, ensure clear comments + +## 6. Security Guidance + +### 6.1 Path Traversal Prevention + +When implementing tools that interact with the local filesystem, always use `resolveLocalPathFunction` to ensure paths remain within the intended directory. + +**Secure Pattern:** +```typescript +import { resolveLocalPathFunction } from './server.getResources'; +// ... +const safePath = await resolveLocalPathFunction(requestedPath, rootDir); + +if (!safePath) { + throw new McpError(ErrorCode.InvalidParams, 'Access denied'); +} +``` + +### 6.2 Plugin Isolation + +- **Strict (Default)**: Isolation is enabled. Restricted environment. +- **None**: Isolation is disabled. Use ONLY when system/network access is strictly required (e.g., `git` commands). +- **Requirement**: Agents MUST warn users when a proposed solution requires `--plugin-isolation none` and explain the security implications. + +## Date and Time Management + +Run `$ date` to get system date before applying dates. Used for: +- Updating timestamps in documentation +- Adding creation dates +- Recording when changes were made diff --git a/guidelines/agent_coding.md b/guidelines/agent_coding.md new file mode 100644 index 0000000..a9de30e --- /dev/null +++ b/guidelines/agent_coding.md @@ -0,0 +1,233 @@ +# Agent Coding + +## Overview + +Coding standards and architectural patterns for the PatternFly MCP project. This document emphasizes maintainability, performance, and pragmatic TypeScript usage. + +## For Agents + +### Processing Priority + +High - This document should be processed when working with source code or implementing features. + +### Related Guidelines + +See the [Guidelines Index](./README.md#guidelines-index) for all guidelines. + +## 1. TypeScript Standards + +Adhere to the [TypeScript coding conventions](../CONTRIBUTING.md#typescript) established for the project. + +### Core Principles + +- **Pragmatic Over Perfection**: Focus on code and functionality. Types are for consumer ergonomics and quick sanity checks — not a blocker for implementation speed. +- **Generics should be reserved**: Use for public exported functions/typings. If they reduce readability, prefer concrete typings. +- **Prefer `unknown` over `any`**: `unknown` is the default at boundaries; add runtime guards and narrow. `any` is still acceptable in testing or deserialization (IPC). +- **Prefer inference over explicit returns**: Let inference work unless the function/type is part of the public surface. +- **Style Preference (Nullish Coalescing)**: Align to the codebase's established style. Prefer logical OR (`||`) over lazy application of the nullish coalescing operator (`??`). Use nullish coalescing when an active distinction for `null`/`undefined` (e.g., distinguishing from `0`, `""`, or `false`) is logically required. +- **Avoid Over-Engineering**: Do not use overabundant type guards or complex nested ternaries. Prefer clear, readable logic over opaque TypeScript patterns. + +### Strict ESM Enforcement + +The project is strictly ESM. Agents MUST follow these rules: +- **Exports**: + - **Internal Source Code (TypeScript)**: Favor named exports (e.g., `export { foo }`). + - **External Tool Plugins (JavaScript)**: MUST use `export default` for the tool definition. +- **Explicit Extensions**: + - **Internal Source Code (TypeScript)**: Use extension-less imports for local modules (e.g., `import { foo } from './foo'`). + - **External Tool Plugins (JavaScript)**: All relative imports MUST include explicit file extensions (e.g., `import { foo } from './foo.js'`) as they are loaded by the Node.js ESM runtime. +- **No CommonJS**: Do not use `require()`, `module.exports`, or `__dirname`. + +### When to Bypass TypeScript (Localized Opt-out) + +Specific modules allow bypassing strict typing to maintain momentum: + +- **Internal tool composition** (`src/server.tools.ts`): Use localized casts for inferred unions that don't narrow. Add a short comment explaining intent. +- **Schema conversion** (`src/server.schema.ts`): Returning `z.ZodTypeAny` is fine. Avoid deep type plumbing. +- **Tools Host IPC** (`src/server.toolsHost.ts`): `any` for deserialized payloads is acceptable. Runtime checks and try/catch are the safety net. +- **Test fixtures and E2E clients**: Use `// @ts-ignore` or `as any` where tests exercise built outputs or where typings aren’t the point under test. + +## 2. Architectural Patterns + +The project follows a plugin-based architecture focused on stability and extensibility. + +### 2.1 Tool and Resource Authoring (The "Creator" Pattern) + +All tools and resources MUST follow the **Creator Pattern** for dependency injection and testability. + +- **Structure**: Creator functions accept an optional `options` parameter (defaults to `getOptions()`) and return a tool/resource tuple. +- **Options Hybrid Approach**: Environment-dependent helpers should accept an optional `options` parameter that defaults to `getOptions()`. This allows for explicit dependency injection in tests while maintaining ergonomics via `AsyncLocalStorage` in production. Pure transforms should remain option-agnostic. +- **Internal Tools**: `(options = getOptions()): McpTool` -> Returns `[name, schema, handler]`. +- **Internal Resources**: `(options = getOptions()): McpResource` -> Returns `[name, uri, config, handler]`. +- **External Tool Plugins**: Authored using the `createMcpTool` helper with an object configuration, exported as `default`. +- **Testing**: Creators allow easy mocking: `const tool = usePatternFlyDocsTool(mockOptions)`. + +### 2.2 Module Organization and Exports + +- **File Naming**: + - **Internal**: `lowerCamelCase` with dot notation (e.g., `server.http.ts`, `tool.docs.ts`). + - **Prefixes**: `server.*` (core), `tool.*` (built-in tools), `resource.*` (resources), `options.*` (config). + - **External/Examples**: `lowerCamelCase` with descriptive prefixes (e.g., `toolPluginHello.js`). +- **Export Patterns**: + - **Internal (TS)**: Use **named exports** grouped at the end of the file. + - **External (JS)**: Use **default export** for the primary tool definition. +- **Concurrency**: Use `processDocsFunction` for multi-file loading to leverage the `promiseQueue` (sliding window pattern). Respect `maxDocsToLoad`. + +### 2.3 External Tool Plugin Scaffold + +External tool plugins should follow this basic structure: + +```javascript +import { createMcpTool } from '@patternfly/patternfly-mcp'; + +export default createMcpTool({ + name: 'myTool', + description: 'Tool description', + inputSchema: { + type: 'object', + properties: { + param1: { type: 'string', description: 'Parameter description' } + }, + required: ['param1'] + }, + async handler({ param1 }) { + return { + content: [{ type: 'text', text: `Result: ${param1}` }] + }; + } +}); +``` + +## 3. Common Patterns + +### 3.1 Memoization + +Expensive operations (network, I/O, schema processing) should be memoized by assigning a `.memo` property to the function. This allows easier testing of the original function. + +- **Usage**: `const result = await getComponentSchema.memo('Button');` +- **Pattern**: +```typescript +const expensiveFn = async (arg: string) => { /* ... */ }; + +expensiveFn.memo = memo(expensiveFn, { + cacheLimit: 10, + keyHash: (args) => args[0] +}); +``` +- **Note**: Use `cacheErrors: false` if normalization should retry on subsequent attempts. + +### 3.2 Context Management + +Use `AsyncLocalStorage` via helper functions to access session and global options without parameter drilling. + +- **Tool Pattern**: +```typescript +const myTool = (options = getOptions()): McpTool => { + const session = getSessionOptions(); + return [name, schema, async (args) => { /* use options/session */ }]; +}; +``` +- **Execution**: Context is automatically preserved across async boundaries. Use `runWithSession` or `runWithOptions` only when explicit overrides are required. + +### 3.3 Error Handling and "Suggestive Failure" + +Always use `McpError` with appropriate `ErrorCode` for user-facing failures. When a resource is not found, provide suggestions. + +**Note on Error Codes**: To avoid drift, we do not maintain a local list of MCP SDK `ErrorCode` values. Agents MUST analyze the current version of the `@modelcontextprotocol/sdk` package to identify correct codes. Avoid listing package-specific resources we do not control in agent documentation due to maintenance concerns. + +```typescript +const { exactMatches, searchResults } = searchComponents.memo(name); + +if (exactMatches.length === 0) { + const suggestions = searchResults.map(r => r.item).slice(0, 3); + + throw new McpError( + ErrorCode.InvalidParams, + `"${name}" not found. Did you mean ${suggestions.map(s => `"${s}"`).join(', ')}?` + ); +} +``` + +### 3.4 Core Utilities (Helpers, Schema, Guards) + +Centralized utilities in `server.helpers.ts`, `server.schema.ts`, and `server.getResources.ts` should be favored over re-implementation. + +- **Helpers**: Use `stringJoin.newline()`, `freezeObject()`, `timeoutFunction()`, and `mergeObjects()`. +- **Schemas**: Use `normalizeInputSchema(schema)` to convert JSON Schema or Zod shapes into valid Zod instances. +- **Type Guards**: Use `isPlainObject()`, `isZodSchema()`, and `isErrorLike()` for runtime narrowing. +- **Zod Detection**: Robustly detect Zod schemas by checking for internal brands: `_def` for Zod v3 and `_zod` for Zod v4. Avoid relying solely on `parse()` or `safeParse()` methods. +- **Immutability**: Options and Session objects are **frozen**; use `structuredClone()` before modification. + +### 3.5 Validation and Sanitization + +Validate input at boundaries (handlers) and sanitize objects before merging. + +```typescript +const handler = async (args: unknown) => { + if (!isPlainObject(args)) { + throw new McpError(ErrorCode.InvalidParams, 'Object required'); + } + + const { name } = args; + + if (typeof name !== 'string' || !name.trim()) { + throw new McpError(ErrorCode.InvalidParams, 'Name string required'); + } + + return process(name.trim()); +}; +``` + +### 3.6 Async Patterns (Return Await) + +Use `return await` ONLY when catching/translating errors in the current layer or when a `finally` block must execute before returning. Otherwise, return the promise directly. + +### 3.7 Concurrency Patterns (Sliding Window) + +The project favors a "sliding window" pattern for promise queues (see `src/server.getResources.ts`) over strict batching. This maintains a constant number of active requests rather than waiting for an entire batch to complete. + +## 4. JSDoc Documentation Standards + +While the codebase emphasizes pragmatism, **public APIs require comprehensive JSDoc** for consumer ergonomics. + +### 4.1 Required Tags for Public APIs + +- **`@param`**, **`@returns`**, **`@throws`**: Standard documentation for function signature. +- **`@property`**: Document properties for interfaces or classes. +- **`@alias`**: Used for stable aliased typings exposed to consumers. +- **`@template`**: Document generic type parameters. +- **`@example`**: Provide usage examples for complex functions. +- **`@note`**: Important implementation details or gotchas. + +### 4.2 JSDoc Format Examples + +```typescript +/** + * Fetches documentation for a PatternFly component. + * + * @param name - Component name (e.g., 'Button') + * @param options - Configuration + * @param options.maxDocs - Max items to load + * @returns Tool tuple [name, schema, handler] + * @throws {McpError} If component not found + * @example + * const tool = usePatternFlyDocsTool(); + * const res = await tool[2]({ name: 'Button' }); + */ +``` + +### 4.3 Internal Code and Best Practices + +- **Internal Code**: Use minimal JSDoc (description, key params, returns). Focus on "why" rather than "what". +- **Conciseness**: Keep descriptions brief but informative. +- **Accuracy**: Update JSDoc immediately when signatures change. +- **Types**: Use TS types in JSDoc ONLY when they add clarity or differ from the implementation. + +## 5. Quality Control & Validation + +Agents MUST validate all code outputs using the project's quality suite: + +1. **Linting**: `npm run test:lint` (Ensures style consistency) +2. **Type Checking**: `npm run test:types` (tsc validation) +3. **Documentation**: `npm run test:spell-docs` (Cspell validation) +4. **Testing**: `npm run test` (Unit) and `npm run test:integration` (E2E) diff --git a/guidelines/agent_testing.md b/guidelines/agent_testing.md new file mode 100644 index 0000000..c984e3b --- /dev/null +++ b/guidelines/agent_testing.md @@ -0,0 +1,62 @@ +# Agent Testing + +## Overview + +This document provides agent guidance on testing procedures and standards for the PatternFly MCP codebase. + +## For Agents + +### Processing Priority + +High - This document should be processed when working with tests or implementing changes that require testing. + +### Related Guidelines + +See the [Guidelines Index](./README.md#guidelines-index) for a complete list of all guidelines. + +## 1. Test Structure and Organization + +Refer to [testing standards](../CONTRIBUTING.md#testing) for project-wide requirements. + +- **Unit Tests (`src/__tests__/*.test.ts`)**: Focus on individual module logic, helpers, and creator functions. +- **E2E Tests (`tests/*.test.ts`)**: Validate full server lifecycle, transport (stdio/http), and tool/resource execution. +- **Integration Tests (`npm run test:integration`)**: Verify interactions between server components. + +## 2. Testing Principles + +- **Focus on Behavior**: Test what the user (MCP client) observes. Verify that tools return the expected content and errors. See [functionality and testing](../CONTRIBUTING.md#functionality-testing) guidance. +- **Pragmatic Typings**: Explicit `any` is allowed in tests to avoid over-modeling mocks and stubs. Avoid "type threading" in tests; do not attempt to perfectly type every mock. Focus on validating observable behavior. Use lightweight local type aliases if needed. +- **Don't Test Dependencies**: Assume `@patternfly` packages and the MCP SDK work as intended. Test our integration and custom logic. +- **Reproducers Required**: Every bug fix must include a test case that reproduces the issue and verifies the fix. +- **Suggestive Failure**: Tools should be tested for "suggestive failure". If a resource is not found, the tool should attempt to suggest the closest match using available metadata or fuzzy matching. + + **Test Example:** + ```typescript + it('returns suggestions for misspelled component name', async () => { + const tool = usePatternFlyDocsTool(); + const result = tool[2]({ name: 'ton' }); // Misspelling "Button" + + await expect(result).rejects.toThrow(/Did you mean "Button"?/); + }); + ``` + +## 3. Mocking and Snapshots + +- **Jest Mocks**: Use `jest.mock()` for file system (`node:fs/promises`) and network (`fetch`) operations. +- **Snapshot Testing**: Use `toMatchSnapshot()` for large or complex tool outputs (e.g., documentation processing results). +- **Snapshot Verification**: Always inspect snapshot changes. Only update snapshots (`-u`) if the change is intentional and correct. +- **Mocking MCP SDK**: When testing server startup, mocks should simulate transport behavior without requiring actual network/IPC overhead where possible. + +## 4. Test Case Requirements + +- **Happy Path**: Verify standard tool/resource usage works as expected. +- **Error Handling**: Verify that invalid parameters, missing files, and network failures return user-friendly error messages (e.g., "Did you mean 'Button'?"). +- **Edge Cases**: Test empty inputs, extreme `maxDocsToLoad` values, and complex URL patterns. +- **Security**: Verify that `resolveLocalPathFunction` correctly prevents path traversal. + +## 5. Execution + +- **Unit Tests**: `npm test` +- **E2E/Integration**: `npm run test:integration` +- **Manual Verification**: Use the [MCP Inspector](../docs/development.md#testing-with-mcp-inspector) to manually verify tool and resource behavior. +- **Coverage**: Ensure new logic is covered by at least one unit test. diff --git a/mcp-config-example.json b/mcp-config-example.json index ac8cff0..09465c0 100644 --- a/mcp-config-example.json +++ b/mcp-config-example.json @@ -3,7 +3,7 @@ "patternfly-docs": { "command": "npx", "args": ["@patternfly/patternfly-mcp"], - "description": "PatternFly React development rules and documentation" + "description": "PatternFly development rules and documentation" } } -} \ No newline at end of file +} diff --git a/package-lock.json b/package-lock.json index 8aa8550..0b8e9e9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ "@typescript-eslint/eslint-plugin": "^8.52.0", "@typescript-eslint/parser": "^8.52.0", "changelog-light": "^3.0.5", + "cspell": "^9.4.0", "eslint": "^9.39.2", "jest": "^30.2.0", "pkgroll": "^2.20.1", @@ -690,6 +691,602 @@ "eslint": ">=9" } }, + "node_modules/@cspell/cspell-bundled-dicts": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/@cspell/cspell-bundled-dicts/-/cspell-bundled-dicts-9.6.0.tgz", + "integrity": "sha512-gLNe9bB+5gMsTEhR9YPE0Wt122HR2EV+Q1j9W+MbwbeBJmpTWrgAP1ZdpvHOg+6LF6x/bD/EC9HfWdd/om8wXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/dict-ada": "^4.1.1", + "@cspell/dict-al": "^1.1.1", + "@cspell/dict-aws": "^4.0.17", + "@cspell/dict-bash": "^4.2.2", + "@cspell/dict-companies": "^3.2.10", + "@cspell/dict-cpp": "^7.0.2", + "@cspell/dict-cryptocurrencies": "^5.0.5", + "@cspell/dict-csharp": "^4.0.8", + "@cspell/dict-css": "^4.0.19", + "@cspell/dict-dart": "^2.3.2", + "@cspell/dict-data-science": "^2.0.13", + "@cspell/dict-django": "^4.1.6", + "@cspell/dict-docker": "^1.1.17", + "@cspell/dict-dotnet": "^5.0.11", + "@cspell/dict-elixir": "^4.0.8", + "@cspell/dict-en_us": "^4.4.27", + "@cspell/dict-en-common-misspellings": "^2.1.11", + "@cspell/dict-en-gb-mit": "^3.1.16", + "@cspell/dict-filetypes": "^3.0.15", + "@cspell/dict-flutter": "^1.1.1", + "@cspell/dict-fonts": "^4.0.5", + "@cspell/dict-fsharp": "^1.1.1", + "@cspell/dict-fullstack": "^3.2.7", + "@cspell/dict-gaming-terms": "^1.1.2", + "@cspell/dict-git": "^3.0.7", + "@cspell/dict-golang": "^6.0.26", + "@cspell/dict-google": "^1.0.9", + "@cspell/dict-haskell": "^4.0.6", + "@cspell/dict-html": "^4.0.14", + "@cspell/dict-html-symbol-entities": "^4.0.5", + "@cspell/dict-java": "^5.0.12", + "@cspell/dict-julia": "^1.1.1", + "@cspell/dict-k8s": "^1.0.12", + "@cspell/dict-kotlin": "^1.1.1", + "@cspell/dict-latex": "^4.0.4", + "@cspell/dict-lorem-ipsum": "^4.0.5", + "@cspell/dict-lua": "^4.0.8", + "@cspell/dict-makefile": "^1.0.5", + "@cspell/dict-markdown": "^2.0.14", + "@cspell/dict-monkeyc": "^1.0.12", + "@cspell/dict-node": "^5.0.8", + "@cspell/dict-npm": "^5.2.29", + "@cspell/dict-php": "^4.1.1", + "@cspell/dict-powershell": "^5.0.15", + "@cspell/dict-public-licenses": "^2.0.15", + "@cspell/dict-python": "^4.2.25", + "@cspell/dict-r": "^2.1.1", + "@cspell/dict-ruby": "^5.1.0", + "@cspell/dict-rust": "^4.1.1", + "@cspell/dict-scala": "^5.0.9", + "@cspell/dict-shell": "^1.1.2", + "@cspell/dict-software-terms": "^5.1.20", + "@cspell/dict-sql": "^2.2.1", + "@cspell/dict-svelte": "^1.0.7", + "@cspell/dict-swift": "^2.0.6", + "@cspell/dict-terraform": "^1.1.3", + "@cspell/dict-typescript": "^3.2.3", + "@cspell/dict-vue": "^3.0.5", + "@cspell/dict-zig": "^1.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@cspell/cspell-json-reporter": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/@cspell/cspell-json-reporter/-/cspell-json-reporter-9.6.0.tgz", + "integrity": "sha512-5sY1lgAXS5xEOsjT5rREMADj7pHIt56XOL7xR80nNl0TwlpZbeBHhoB2aH5sirVTeodJFN5iraXNbVOYPPupPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/cspell-types": "9.6.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@cspell/cspell-pipe": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/@cspell/cspell-pipe/-/cspell-pipe-9.6.0.tgz", + "integrity": "sha512-YNuY8NNXfE+8Qzknm2ps6QbrZLZu6rSZTZr3dYW3K6TK7+IFVlJ6e2Z9iKJTqp6aZ4AGU/r9QYGmNX4Oq4gZ0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/@cspell/cspell-resolver": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/@cspell/cspell-resolver/-/cspell-resolver-9.6.0.tgz", + "integrity": "sha512-Gb2UWNmRpTOQGpYL4Q/LMw+b50KcRZcf/wJg6w0Yl3IT+F/uDNhNh1f5rHuTyGsbMsMxHJhsb2AoP+73GlbIfw==", + "dev": true, + "license": "MIT", + "dependencies": { + "global-directory": "^4.0.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@cspell/cspell-service-bus": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/@cspell/cspell-service-bus/-/cspell-service-bus-9.6.0.tgz", + "integrity": "sha512-DCuKKkySTEB8MPLTdoPMdmakYcx7XCsHz1YEMbzOcLqJCxXsRlRZg4qE9kRBee/2QY7eYA2kaYNgn/TDMooa4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/@cspell/cspell-types": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/@cspell/cspell-types/-/cspell-types-9.6.0.tgz", + "integrity": "sha512-JTqrD47tV+rWc1y2W8T0NTfWLQMlSWX4OF64/Jf3WbsOD+4UXVIfjRlzPry7+1Zekm6pa38+23jkDBytYpu8yw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/@cspell/dict-ada": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-ada/-/dict-ada-4.1.1.tgz", + "integrity": "sha512-E+0YW9RhZod/9Qy2gxfNZiHJjCYFlCdI69br1eviQQWB8yOTJX0JHXLs79kOYhSW0kINPVUdvddEBe6Lu6CjGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-al": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-al/-/dict-al-1.1.1.tgz", + "integrity": "sha512-sD8GCaZetgQL4+MaJLXqbzWcRjfKVp8x+px3HuCaaiATAAtvjwUQ5/Iubiqwfd1boIh2Y1/3EgM3TLQ7Q8e0wQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-aws": { + "version": "4.0.17", + "resolved": "https://registry.npmjs.org/@cspell/dict-aws/-/dict-aws-4.0.17.tgz", + "integrity": "sha512-ORcblTWcdlGjIbWrgKF+8CNEBQiLVKdUOFoTn0KPNkAYnFcdPP0muT4892h7H4Xafh3j72wqB4/loQ6Nti9E/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-bash": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-bash/-/dict-bash-4.2.2.tgz", + "integrity": "sha512-kyWbwtX3TsCf5l49gGQIZkRLaB/P8g73GDRm41Zu8Mv51kjl2H7Au0TsEvHv7jzcsRLS6aUYaZv6Zsvk1fOz+Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/dict-shell": "1.1.2" + } + }, + "node_modules/@cspell/dict-companies": { + "version": "3.2.10", + "resolved": "https://registry.npmjs.org/@cspell/dict-companies/-/dict-companies-3.2.10.tgz", + "integrity": "sha512-bJ1qnO1DkTn7JYGXvxp8FRQc4yq6tRXnrII+jbP8hHmq5TX5o1Wu+rdfpoUQaMWTl6balRvcMYiINDesnpR9Bw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-cpp": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-cpp/-/dict-cpp-7.0.2.tgz", + "integrity": "sha512-dfbeERiVNeqmo/npivdR6rDiBCqZi3QtjH2Z0HFcXwpdj6i97dX1xaKyK2GUsO/p4u1TOv63Dmj5Vm48haDpuA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-cryptocurrencies": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-cryptocurrencies/-/dict-cryptocurrencies-5.0.5.tgz", + "integrity": "sha512-R68hYYF/rtlE6T/dsObStzN5QZw+0aQBinAXuWCVqwdS7YZo0X33vGMfChkHaiCo3Z2+bkegqHlqxZF4TD3rUA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-csharp": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-csharp/-/dict-csharp-4.0.8.tgz", + "integrity": "sha512-qmk45pKFHSxckl5mSlbHxmDitSsGMlk/XzFgt7emeTJWLNSTUK//MbYAkBNRtfzB4uD7pAFiKgpKgtJrTMRnrQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-css": { + "version": "4.0.19", + "resolved": "https://registry.npmjs.org/@cspell/dict-css/-/dict-css-4.0.19.tgz", + "integrity": "sha512-VYHtPnZt/Zd/ATbW3rtexWpBnHUohUrQOHff/2JBhsVgxOrksAxJnLAO43Q1ayLJBJUUwNVo+RU0sx0aaysZfg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-dart": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-dart/-/dict-dart-2.3.2.tgz", + "integrity": "sha512-sUiLW56t9gfZcu8iR/5EUg+KYyRD83Cjl3yjDEA2ApVuJvK1HhX+vn4e4k4YfjpUQMag8XO2AaRhARE09+/rqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-data-science": { + "version": "2.0.13", + "resolved": "https://registry.npmjs.org/@cspell/dict-data-science/-/dict-data-science-2.0.13.tgz", + "integrity": "sha512-l1HMEhBJkPmw4I2YGVu2eBSKM89K9pVF+N6qIr5Uo5H3O979jVodtuwP8I7LyPrJnC6nz28oxeGRCLh9xC5CVA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-django": { + "version": "4.1.6", + "resolved": "https://registry.npmjs.org/@cspell/dict-django/-/dict-django-4.1.6.tgz", + "integrity": "sha512-SdbSFDGy9ulETqNz15oWv2+kpWLlk8DJYd573xhIkeRdcXOjskRuxjSZPKfW7O3NxN/KEf3gm3IevVOiNuFS+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-docker": { + "version": "1.1.17", + "resolved": "https://registry.npmjs.org/@cspell/dict-docker/-/dict-docker-1.1.17.tgz", + "integrity": "sha512-OcnVTIpHIYYKhztNTyK8ShAnXTfnqs43hVH6p0py0wlcwRIXe5uj4f12n7zPf2CeBI7JAlPjEsV0Rlf4hbz/xQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-dotnet": { + "version": "5.0.11", + "resolved": "https://registry.npmjs.org/@cspell/dict-dotnet/-/dict-dotnet-5.0.11.tgz", + "integrity": "sha512-LSVKhpFf/ASTWJcfYeS0Sykcl1gVMsv2Z5Eo0TnTMSTLV3738HH+66pIsjUTChqU6SF3gKPuCe6EOaRYqb/evA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-elixir": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-elixir/-/dict-elixir-4.0.8.tgz", + "integrity": "sha512-CyfphrbMyl4Ms55Vzuj+mNmd693HjBFr9hvU+B2YbFEZprE5AG+EXLYTMRWrXbpds4AuZcvN3deM2XVB80BN/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-en_us": { + "version": "4.4.27", + "resolved": "https://registry.npmjs.org/@cspell/dict-en_us/-/dict-en_us-4.4.27.tgz", + "integrity": "sha512-0y4vH2i5cFmi8sxkc4OlD2IlnqDznOtKczm4h6jA288g5VVrm3bhkYK6vcB8b0CoRKtYWKet4VEmHBP1yI+Qfw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-en-common-misspellings": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/@cspell/dict-en-common-misspellings/-/dict-en-common-misspellings-2.1.11.tgz", + "integrity": "sha512-2jcY494If1udvzd7MT2z/QH/RACUo/I02vIY4ttNdZhgYvUmRKhg8OBdrbzYo0lJOcc7XUb8rhIFQRHzxOSVeA==", + "dev": true, + "license": "CC BY-SA 4.0" + }, + "node_modules/@cspell/dict-en-gb-mit": { + "version": "3.1.16", + "resolved": "https://registry.npmjs.org/@cspell/dict-en-gb-mit/-/dict-en-gb-mit-3.1.16.tgz", + "integrity": "sha512-4PPdapCJslytxAVJu35Mv97qDyGmAQxtDE790T2bWNhcqN6gvRVAc/eTRaXkUIf21q1xCxxNNqpH4VfMup69rQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-filetypes": { + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@cspell/dict-filetypes/-/dict-filetypes-3.0.15.tgz", + "integrity": "sha512-uDMeqYlLlK476w/muEFQGBy9BdQWS0mQ7BJiy/iQv5XUWZxE2O54ZQd9nW8GyQMzAgoyg5SG4hf9l039Qt66oA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-flutter": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-flutter/-/dict-flutter-1.1.1.tgz", + "integrity": "sha512-UlOzRcH2tNbFhZmHJN48Za/2/MEdRHl2BMkCWZBYs+30b91mWvBfzaN4IJQU7dUZtowKayVIF9FzvLZtZokc5A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-fonts": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-fonts/-/dict-fonts-4.0.5.tgz", + "integrity": "sha512-BbpkX10DUX/xzHs6lb7yzDf/LPjwYIBJHJlUXSBXDtK/1HaeS+Wqol4Mlm2+NAgZ7ikIE5DQMViTgBUY3ezNoQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-fsharp": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-fsharp/-/dict-fsharp-1.1.1.tgz", + "integrity": "sha512-imhs0u87wEA4/cYjgzS0tAyaJpwG7vwtC8UyMFbwpmtw+/bgss+osNfyqhYRyS/ehVCWL17Ewx2UPkexjKyaBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-fullstack": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@cspell/dict-fullstack/-/dict-fullstack-3.2.7.tgz", + "integrity": "sha512-IxEk2YAwAJKYCUEgEeOg3QvTL4XLlyArJElFuMQevU1dPgHgzWElFevN5lsTFnvMFA1riYsVinqJJX0BanCFEg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-gaming-terms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-gaming-terms/-/dict-gaming-terms-1.1.2.tgz", + "integrity": "sha512-9XnOvaoTBscq0xuD6KTEIkk9hhdfBkkvJAIsvw3JMcnp1214OCGW8+kako5RqQ2vTZR3Tnf3pc57o7VgkM0q1Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-git": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/@cspell/dict-git/-/dict-git-3.0.7.tgz", + "integrity": "sha512-odOwVKgfxCQfiSb+nblQZc4ErXmnWEnv8XwkaI4sNJ7cNmojnvogYVeMqkXPjvfrgEcizEEA4URRD2Ms5PDk1w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-golang": { + "version": "6.0.26", + "resolved": "https://registry.npmjs.org/@cspell/dict-golang/-/dict-golang-6.0.26.tgz", + "integrity": "sha512-YKA7Xm5KeOd14v5SQ4ll6afe9VSy3a2DWM7L9uBq4u3lXToRBQ1W5PRa+/Q9udd+DTURyVVnQ+7b9cnOlNxaRg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-google": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@cspell/dict-google/-/dict-google-1.0.9.tgz", + "integrity": "sha512-biL65POqialY0i4g6crj7pR6JnBkbsPovB2WDYkj3H4TuC/QXv7Pu5pdPxeUJA6TSCHI7T5twsO4VSVyRxD9CA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-haskell": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@cspell/dict-haskell/-/dict-haskell-4.0.6.tgz", + "integrity": "sha512-ib8SA5qgftExpYNjWhpYIgvDsZ/0wvKKxSP+kuSkkak520iPvTJumEpIE+qPcmJQo4NzdKMN8nEfaeci4OcFAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-html": { + "version": "4.0.14", + "resolved": "https://registry.npmjs.org/@cspell/dict-html/-/dict-html-4.0.14.tgz", + "integrity": "sha512-2bf7n+kS92g+cMKV0wr9o/Oq9n8JzU7CcrB96gIh2GHgnF+0xDOqO2W/1KeFAqOfqosoOVE48t+4dnEMkkoJ2Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-html-symbol-entities": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-html-symbol-entities/-/dict-html-symbol-entities-4.0.5.tgz", + "integrity": "sha512-429alTD4cE0FIwpMucvSN35Ld87HCyuM8mF731KU5Rm4Je2SG6hmVx7nkBsLyrmH3sQukTcr1GaiZsiEg8svPA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-java": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/@cspell/dict-java/-/dict-java-5.0.12.tgz", + "integrity": "sha512-qPSNhTcl7LGJ5Qp6VN71H8zqvRQK04S08T67knMq9hTA8U7G1sTKzLmBaDOFhq17vNX/+rT+rbRYp+B5Nwza1A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-julia": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-julia/-/dict-julia-1.1.1.tgz", + "integrity": "sha512-WylJR9TQ2cgwd5BWEOfdO3zvDB+L7kYFm0I9u0s9jKHWQ6yKmfKeMjU9oXxTBxIufhCXm92SKwwVNAC7gjv+yA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-k8s": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@cspell/dict-k8s/-/dict-k8s-1.0.12.tgz", + "integrity": "sha512-2LcllTWgaTfYC7DmkMPOn9GsBWsA4DZdlun4po8s2ysTP7CPEnZc1ZfK6pZ2eI4TsZemlUQQ+NZxMe9/QutQxg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-kotlin": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-kotlin/-/dict-kotlin-1.1.1.tgz", + "integrity": "sha512-J3NzzfgmxRvEeOe3qUXnSJQCd38i/dpF9/t3quuWh6gXM+krsAXP75dY1CzDmS8mrJAlBdVBeAW5eAZTD8g86Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-latex": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@cspell/dict-latex/-/dict-latex-4.0.4.tgz", + "integrity": "sha512-YdTQhnTINEEm/LZgTzr9Voz4mzdOXH7YX+bSFs3hnkUHCUUtX/mhKgf1CFvZ0YNM2afjhQcmLaR9bDQVyYBvpA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-lorem-ipsum": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-lorem-ipsum/-/dict-lorem-ipsum-4.0.5.tgz", + "integrity": "sha512-9a4TJYRcPWPBKkQAJ/whCu4uCAEgv/O2xAaZEI0n4y1/l18Yyx8pBKoIX5QuVXjjmKEkK7hi5SxyIsH7pFEK9Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-lua": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-lua/-/dict-lua-4.0.8.tgz", + "integrity": "sha512-N4PkgNDMu9JVsRu7JBS/3E/dvfItRgk9w5ga2dKq+JupP2Y3lojNaAVFhXISh4Y0a6qXDn2clA6nvnavQ/jjLA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-makefile": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-makefile/-/dict-makefile-1.0.5.tgz", + "integrity": "sha512-4vrVt7bGiK8Rx98tfRbYo42Xo2IstJkAF4tLLDMNQLkQ86msDlYSKG1ZCk8Abg+EdNcFAjNhXIiNO+w4KflGAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-markdown": { + "version": "2.0.14", + "resolved": "https://registry.npmjs.org/@cspell/dict-markdown/-/dict-markdown-2.0.14.tgz", + "integrity": "sha512-uLKPNJsUcumMQTsZZgAK9RgDLyQhUz/uvbQTEkvF/Q4XfC1i/BnA8XrOrd0+Vp6+tPOKyA+omI5LRWfMu5K/Lw==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@cspell/dict-css": "^4.0.19", + "@cspell/dict-html": "^4.0.14", + "@cspell/dict-html-symbol-entities": "^4.0.5", + "@cspell/dict-typescript": "^3.2.3" + } + }, + "node_modules/@cspell/dict-monkeyc": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/@cspell/dict-monkeyc/-/dict-monkeyc-1.0.12.tgz", + "integrity": "sha512-MN7Vs11TdP5mbdNFQP5x2Ac8zOBm97ARg6zM5Sb53YQt/eMvXOMvrep7+/+8NJXs0jkp70bBzjqU4APcqBFNAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-node": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/@cspell/dict-node/-/dict-node-5.0.8.tgz", + "integrity": "sha512-AirZcN2i84ynev3p2/1NCPEhnNsHKMz9zciTngGoqpdItUb2bDt1nJBjwlsrFI78GZRph/VaqTVFwYikmncpXg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-npm": { + "version": "5.2.29", + "resolved": "https://registry.npmjs.org/@cspell/dict-npm/-/dict-npm-5.2.29.tgz", + "integrity": "sha512-ZAef8JpYmbuHFT1zekj/YyImLPvZevjECw663EmG5GPePyNo4AfH8Dd2fFhaOyQ3P5I5LrkAhGwypnOfUxcssw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-php": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-php/-/dict-php-4.1.1.tgz", + "integrity": "sha512-EXelI+4AftmdIGtA8HL8kr4WlUE11OqCSVlnIgZekmTkEGSZdYnkFdiJ5IANSALtlQ1mghKjz+OFqVs6yowgWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-powershell": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/@cspell/dict-powershell/-/dict-powershell-5.0.15.tgz", + "integrity": "sha512-l4S5PAcvCFcVDMJShrYD0X6Huv9dcsQPlsVsBGbH38wvuN7gS7+GxZFAjTNxDmTY1wrNi1cCatSg6Pu2BW4rgg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-public-licenses": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@cspell/dict-public-licenses/-/dict-public-licenses-2.0.15.tgz", + "integrity": "sha512-cJEOs901H13Pfy0fl4dCD1U+xpWIMaEPq8MeYU83FfDZvellAuSo4GqWCripfIqlhns/L6+UZEIJSOZnjgy7Wg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-python": { + "version": "4.2.25", + "resolved": "https://registry.npmjs.org/@cspell/dict-python/-/dict-python-4.2.25.tgz", + "integrity": "sha512-hDdN0YhKgpbtZVRjQ2c8jk+n0wQdidAKj1Fk8w7KEHb3YlY5uPJ0mAKJk7AJKPNLOlILoUmN+HAVJz+cfSbWYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/dict-data-science": "^2.0.13" + } + }, + "node_modules/@cspell/dict-r": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-r/-/dict-r-2.1.1.tgz", + "integrity": "sha512-71Ka+yKfG4ZHEMEmDxc6+blFkeTTvgKbKAbwiwQAuKl3zpqs1Y0vUtwW2N4b3LgmSPhV3ODVY0y4m5ofqDuKMw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-ruby": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-ruby/-/dict-ruby-5.1.0.tgz", + "integrity": "sha512-9PJQB3cfkBULrMLp5kSAcFPpzf8oz9vFN+QYZABhQwWkGbuzCIXSorHrmWSASlx4yejt3brjaWS57zZ/YL5ZQQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-rust": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-rust/-/dict-rust-4.1.1.tgz", + "integrity": "sha512-fXiXnZH0wOaEVTKFRNaz6TsUGhuB8dAT0ubYkDNzRQCaV5JGSOebGb1v2x5ZrOSVp+moxWM/vdBfiNU6KOEaFQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-scala": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/@cspell/dict-scala/-/dict-scala-5.0.9.tgz", + "integrity": "sha512-AjVcVAELgllybr1zk93CJ5wSUNu/Zb5kIubymR/GAYkMyBdYFCZ3Zbwn4Zz8GJlFFAbazABGOu0JPVbeY59vGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-shell": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@cspell/dict-shell/-/dict-shell-1.1.2.tgz", + "integrity": "sha512-WqOUvnwcHK1X61wAfwyXq04cn7KYyskg90j4lLg3sGGKMW9Sq13hs91pqrjC44Q+lQLgCobrTkMDw9Wyl9nRFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-software-terms": { + "version": "5.1.20", + "resolved": "https://registry.npmjs.org/@cspell/dict-software-terms/-/dict-software-terms-5.1.20.tgz", + "integrity": "sha512-TEk1xHvetTI4pv7Vzje1D322m6QEjaH2P6ucOOf6q7EJCppQIdC0lZSXkgHJAFU5HGSvEXSzvnVeW2RHW86ziQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-sql": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@cspell/dict-sql/-/dict-sql-2.2.1.tgz", + "integrity": "sha512-qDHF8MpAYCf4pWU8NKbnVGzkoxMNrFqBHyG/dgrlic5EQiKANCLELYtGlX5auIMDLmTf1inA0eNtv74tyRJ/vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-svelte": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@cspell/dict-svelte/-/dict-svelte-1.0.7.tgz", + "integrity": "sha512-hGZsGqP0WdzKkdpeVLBivRuSNzOTvN036EBmpOwxH+FTY2DuUH7ecW+cSaMwOgmq5JFSdTcbTNFlNC8HN8lhaQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-swift": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@cspell/dict-swift/-/dict-swift-2.0.6.tgz", + "integrity": "sha512-PnpNbrIbex2aqU1kMgwEKvCzgbkHtj3dlFLPMqW1vSniop7YxaDTtvTUO4zA++ugYAEL+UK8vYrBwDPTjjvSnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-terraform": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-terraform/-/dict-terraform-1.1.3.tgz", + "integrity": "sha512-gr6wxCydwSFyyBKhBA2xkENXtVFToheqYYGFvlMZXWjviynXmh+NK/JTvTCk/VHk3+lzbO9EEQKee6VjrAUSbA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-typescript": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/@cspell/dict-typescript/-/dict-typescript-3.2.3.tgz", + "integrity": "sha512-zXh1wYsNljQZfWWdSPYwQhpwiuW0KPW1dSd8idjMRvSD0aSvWWHoWlrMsmZeRl4qM4QCEAjua8+cjflm41cQBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-vue": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@cspell/dict-vue/-/dict-vue-3.0.5.tgz", + "integrity": "sha512-Mqutb8jbM+kIcywuPQCCaK5qQHTdaByoEO2J9LKFy3sqAdiBogNkrplqUK0HyyRFgCfbJUgjz3N85iCMcWH0JA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dict-zig": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@cspell/dict-zig/-/dict-zig-1.0.0.tgz", + "integrity": "sha512-XibBIxBlVosU06+M6uHWkFeT0/pW5WajDRYdXG2CgHnq85b0TI/Ks0FuBJykmsgi2CAD3Qtx8UHFEtl/DSFnAQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspell/dynamic-import": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/@cspell/dynamic-import/-/dynamic-import-9.6.0.tgz", + "integrity": "sha512-Lkn82wyGj2ltxeYfH2bEjshdes1fx3ouYUZxeW5i6SBBvEVJoSmr43AygI8A317UMIQxVj59qVBorrtGYcRI1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/url": "9.6.0", + "import-meta-resolve": "^4.2.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@cspell/filetypes": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/@cspell/filetypes/-/filetypes-9.6.0.tgz", + "integrity": "sha512-CaWyk5j20H6sr+HCArtUY95jUQb7A/6W0GC4B4umnqoWvgqwR72duowLFa+w1K2C7tZg3GoV4Wf2cUn9jjt5FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/@cspell/strong-weak-map": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/@cspell/strong-weak-map/-/strong-weak-map-9.6.0.tgz", + "integrity": "sha512-9g8LCLv/2RrprGeGnFAaBETWq7ESnBcoMbvgNu+vZE58iF+pbFvP0qGgKvVeKEEpc2LZhNuHLsUH37MUS6DOQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/@cspell/url": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/@cspell/url/-/url-9.6.0.tgz", + "integrity": "sha512-257WOxh9vOYHAVgBNXRCdLEd+ldzlVbzcc9u+6DYoCDCNGe0OvOWOGsAfnUbMc9xEw48XgBlDYgOlPbjWGLOTg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -3737,6 +4334,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/array-timsort": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-timsort/-/array-timsort-1.0.3.tgz", + "integrity": "sha512-/+3GRL7dDAGEfM6TseQk/U+mi18TU2Ms9I3UlLdUMhz2hbvGNTKdj9xniwXfUqgYhHxRx0+8UnKkvlNwVU+cWQ==", + "dev": true, + "license": "MIT" + }, "node_modules/array.prototype.findlast": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", @@ -4275,6 +4879,35 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chalk-template": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/chalk-template/-/chalk-template-1.1.2.tgz", + "integrity": "sha512-2bxTP2yUH7AJj/VAXfcA+4IcWGdQ87HwBANLt5XxGTeomo8yG0y95N1um9i5StvhT/Bl0/2cARA5v1PpPXUxUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.2.0" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/chalk/chalk-template?sponsor=1" + } + }, + "node_modules/chalk-template/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/change-case": { "version": "5.4.4", "resolved": "https://registry.npmjs.org/change-case/-/change-case-5.4.4.tgz", @@ -4369,6 +5002,36 @@ "node": ">=0.8.0" } }, + "node_modules/clear-module": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/clear-module/-/clear-module-4.1.2.tgz", + "integrity": "sha512-LWAxzHqdHsAZlPlEyJ2Poz6AIs384mPeqLVCru2p0BrP9G/kVGuhNyZYClLO6cXlnuJjzC8xtsJIuMjKqLXoAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^2.0.0", + "resolve-from": "^5.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clear-module/node_modules/parent-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-2.0.0.tgz", + "integrity": "sha512-uo0Z9JJeWzv8BG+tRcapBKNJ0dro9cLyczGzulS6EfeyAdeC9sbojtW6XwvYxJkEne9En+J2XEl4zyglVeIwFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", @@ -4485,6 +5148,31 @@ "dev": true, "license": "MIT" }, + "node_modules/commander": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.2.tgz", + "integrity": "sha512-TywoWNNRbhoD0BXs1P3ZEScW8W5iKrnbithIl0YH+uCmBd0QpPOA8yc82DS3BIE5Ma6FnBVUsJ7wVUDz4dvOWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/comment-json": { + "version": "4.5.1", + "resolved": "https://registry.npmjs.org/comment-json/-/comment-json-4.5.1.tgz", + "integrity": "sha512-taEtr3ozUmOB7it68Jll7s0Pwm+aoiHyXKrEC8SEodL4rNpdfDLqa7PfBlrgFoCNNdR8ImL+muti5IGvktJAAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-timsort": "^1.0.3", + "core-util-is": "^1.0.3", + "esprima": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/comment-parser": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", @@ -4576,6 +5264,13 @@ "url": "https://opencollective.com/core-js" } }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, "node_modules/cors": { "version": "2.8.5", "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", @@ -4610,6 +5305,225 @@ "node": ">= 8" } }, + "node_modules/cspell": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/cspell/-/cspell-9.6.0.tgz", + "integrity": "sha512-Mpf0oT2KAHTIb3YPAXWhW64/4CZKW5Lka4j1YxCLK3jM3nenmIsY/ocrJvqCMF4+1eejRF0N55sT3XmrijI5YQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/cspell-json-reporter": "9.6.0", + "@cspell/cspell-pipe": "9.6.0", + "@cspell/cspell-types": "9.6.0", + "@cspell/dynamic-import": "9.6.0", + "@cspell/url": "9.6.0", + "ansi-regex": "^6.2.2", + "chalk": "^5.6.2", + "chalk-template": "^1.1.2", + "commander": "^14.0.2", + "cspell-config-lib": "9.6.0", + "cspell-dictionary": "9.6.0", + "cspell-gitignore": "9.6.0", + "cspell-glob": "9.6.0", + "cspell-io": "9.6.0", + "cspell-lib": "9.6.0", + "fast-json-stable-stringify": "^2.1.0", + "flatted": "^3.3.3", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15" + }, + "bin": { + "cspell": "bin.mjs", + "cspell-esm": "bin.mjs" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/streetsidesoftware/cspell?sponsor=1" + } + }, + "node_modules/cspell-config-lib": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/cspell-config-lib/-/cspell-config-lib-9.6.0.tgz", + "integrity": "sha512-5ztvheawkmFXNHGN82iOOntU3T5mmlQBP/plgoKdBZ6+lSYrOJLkOyqxYyi7MrUBDtWrXPzFllkBrPNRDlbX/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/cspell-types": "9.6.0", + "comment-json": "^4.5.1", + "smol-toml": "^1.6.0", + "yaml": "^2.8.2" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cspell-dictionary": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/cspell-dictionary/-/cspell-dictionary-9.6.0.tgz", + "integrity": "sha512-wW0m1kLrbK6bRY/GrLUGKUUJ1Z4ZUgIb8LD4zNaECcvGviv9V7VcR3mEwUip3ZjoHa3ClzEoWgQ9gXrtac80Wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/cspell-pipe": "9.6.0", + "@cspell/cspell-types": "9.6.0", + "cspell-trie-lib": "9.6.0", + "fast-equals": "^6.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cspell-gitignore": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/cspell-gitignore/-/cspell-gitignore-9.6.0.tgz", + "integrity": "sha512-8GfmJuRBBvibyPHnNE2wYJAiQ/ceDYLD1X1sUQaCyj6hPMR7ChJiVhFPtS11hMqkjZ46OBMYTMGWqO792L9fEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/url": "9.6.0", + "cspell-glob": "9.6.0", + "cspell-io": "9.6.0" + }, + "bin": { + "cspell-gitignore": "bin.mjs" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cspell-glob": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/cspell-glob/-/cspell-glob-9.6.0.tgz", + "integrity": "sha512-KmEbKN0qdEamsEYbkFu7zjLYfw3hMmn9kmeh94IHr2kq6vWq5vNP5l1BuqmrUeFZlbNd07vj08IKAZHYsoGheQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/url": "9.6.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cspell-glob/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/cspell-grammar": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/cspell-grammar/-/cspell-grammar-9.6.0.tgz", + "integrity": "sha512-jZVIM5/3eB9rWURDq+VXdYip+DmPuFzO+bqaRtzqT8w6YoOIGYbiIxdwvyyA9xdH7SmW8uqHJP5x4pzZju1lNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/cspell-pipe": "9.6.0", + "@cspell/cspell-types": "9.6.0" + }, + "bin": { + "cspell-grammar": "bin.mjs" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cspell-io": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/cspell-io/-/cspell-io-9.6.0.tgz", + "integrity": "sha512-wZuZzKOYIb698kVEINYjGaNFQu+AFZ945TORM3hapmPjez+vsHwl8m/pPpCHeGMpQtHMEDkX84AbQ7R55MRIwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/cspell-service-bus": "9.6.0", + "@cspell/url": "9.6.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cspell-lib": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/cspell-lib/-/cspell-lib-9.6.0.tgz", + "integrity": "sha512-m9rIv8hkQ3Dio4s80HQbM9cdxENcd6pS8j2AHWL50OSjJf3Xhw6/wMrIAGbwLHP15K6QZVU2eJ/abCzIJwjA4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspell/cspell-bundled-dicts": "9.6.0", + "@cspell/cspell-pipe": "9.6.0", + "@cspell/cspell-resolver": "9.6.0", + "@cspell/cspell-types": "9.6.0", + "@cspell/dynamic-import": "9.6.0", + "@cspell/filetypes": "9.6.0", + "@cspell/strong-weak-map": "9.6.0", + "@cspell/url": "9.6.0", + "clear-module": "^4.1.2", + "cspell-config-lib": "9.6.0", + "cspell-dictionary": "9.6.0", + "cspell-glob": "9.6.0", + "cspell-grammar": "9.6.0", + "cspell-io": "9.6.0", + "cspell-trie-lib": "9.6.0", + "env-paths": "^3.0.0", + "gensequence": "^8.0.8", + "import-fresh": "^3.3.1", + "resolve-from": "^5.0.0", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-uri": "^3.1.0", + "xdg-basedir": "^5.1.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cspell-trie-lib": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/cspell-trie-lib/-/cspell-trie-lib-9.6.0.tgz", + "integrity": "sha512-L7GSff5F9cF60QT78WsebVlb3sppi6jbvTHwsw7WF1jUN/ioAo7OzBYtYB7xkYeejcdVEpqfvf/ZOXPDp8x2Wg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "@cspell/cspell-types": "9.6.0" + } + }, + "node_modules/cspell/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cspell/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", @@ -4875,6 +5789,19 @@ "node": ">=10.13.0" } }, + "node_modules/env-paths": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/error-ex": { "version": "1.3.4", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", @@ -6151,6 +7078,16 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, + "node_modules/fast-equals": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-6.0.0.tgz", + "integrity": "sha512-PFhhIGgdM79r5Uztdj9Zb6Tt1zKafqVfdMGwVca1z5z6fbX7DmsySSuJd8HiP6I1j505DCS83cLxo5rmSNeVEA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/fast-glob": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", @@ -6456,6 +7393,16 @@ "node": ">= 0.4" } }, + "node_modules/gensequence": { + "version": "8.0.8", + "resolved": "https://registry.npmjs.org/gensequence/-/gensequence-8.0.8.tgz", + "integrity": "sha512-omMVniXEXpdx/vKxGnPRoO2394Otlze28TyxECbFVyoSpZ9H3EO7lemjcB12OpQJzRW4e5tt/dL1rOxry6aMHg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", @@ -6601,6 +7548,22 @@ "node": ">= 6" } }, + "node_modules/global-directory": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz", + "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ini": "4.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/globals": { "version": "16.5.0", "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", @@ -6896,6 +7859,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/import-meta-resolve": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.2.0.tgz", + "integrity": "sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==", + "dev": true, + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + } + }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -6937,6 +7911,16 @@ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC" }, + "node_modules/ini": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz", + "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } + }, "node_modules/internal-slot": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", @@ -10091,6 +11075,19 @@ "node": ">=8" } }, + "node_modules/smol-toml": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/smol-toml/-/smol-toml-1.6.0.tgz", + "integrity": "sha512-4zemZi0HvTnYwLfrpk/CF9LOd9Lt87kAt50GnqhMpyF9U3poDAP2+iukq2bZsO/ufegbYehBkqINbsWxj4l4cw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">= 18" + }, + "funding": { + "url": "https://github.com/sponsors/cyyynthia" + } + }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -12026,6 +13023,19 @@ "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, + "node_modules/xdg-basedir": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-5.1.0.tgz", + "integrity": "sha512-GCPAHLvrIH13+c0SuacwvRYj2SxJXQ4kaVTT5xgL3kPrz56XxkF21IGhjSE1+W0aw7gpBWRGXLCPnPby6lSpmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", diff --git a/package.json b/package.json index 0bf6440..5c4cd3d 100644 --- a/package.json +++ b/package.json @@ -31,12 +31,14 @@ "release": "changelog --non-cc --link-url https://github.com/patternfly/patternfly-mcp.git", "start": "node dist/cli.js --log-stderr", "start:dev": "tsx watch src/cli.ts --verbose --log-stderr", - "test": "npm run test:lint && npm run test:types && jest --selectProjects unit --roots=src/", + "test": "npm run test:spell && npm run test:spell-docs && npm run test:lint && npm run test:types && jest --selectProjects unit --roots=src/", "test:dev": "npm test -- --watchAll", "test:integration": "npm run build && NODE_OPTIONS='--experimental-vm-modules' jest --selectProjects e2e --roots=tests/", "test:integration-dev": "npm run test:integration -- --watchAll", "test:lint": "eslint .", "test:lint-fix": "eslint . --fix", + "test:spell-docs": "cspell './README.md' './CONTRIBUTING.md' './docs/**/*.md' './guidelines/**/*.md' './docs/**/*.ts' './docs/**/*.js' --config ./cspell.config.json --fail-fast", + "test:spell": "cspell './src/**/*.ts' './tests/**/*.ts' --exclude './src/**/*test*' --exclude './tests/**/*test*' --config ./cspell.config.json --fail-fast", "test:types": "tsc --noEmit", "prepare": "npm run build", "prepack": "npm run build" @@ -66,6 +68,7 @@ "@typescript-eslint/eslint-plugin": "^8.52.0", "@typescript-eslint/parser": "^8.52.0", "changelog-light": "^3.0.5", + "cspell": "^9.4.0", "eslint": "^9.39.2", "jest": "^30.2.0", "pkgroll": "^2.20.1", diff --git a/tsconfig.json b/tsconfig.json index 8c47af0..2b3c4a8 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -28,7 +28,8 @@ "tests/**/*", "jest.setupTests.ts", "jest.config.ts", - "types/**/*.d.ts" + "types/**/*.d.ts", + "docs/**/*.ts" ], "exclude": [ "node_modules",