{
  "markdown": "# hudu-mcp\n\n[![CI](https://github.com/WYRE-AI/hudu-mcp/actions/workflows/ci.yml/badge.svg)](https://github.com/WYRE-AI/hudu-mcp/actions/workflows/ci.yml)\n[![License: Apache-2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n[![Node.js](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen.svg)](https://nodejs.org/)\n\nMCP (Model Context Protocol) server for [Hudu](https://www.huduapp.com/) IT documentation platform. Provides 39 tools and 6 resources for managing companies, assets, articles, passwords, websites, and more through any MCP-compatible client.\n\n## Features\n\n- **39 MCP tools** covering all major Hudu resources\n- **6 MCP resources** for direct data access\n- **Dual auth mode**: classic `HUDU_API_KEY` REST access, or `oauth` — a thin,\n  authenticated proxy to a newer Hudu instance's own native MCP server\n- **Dual transport** support: stdio (default) and HTTP Streamable\n- **Lazy initialization** - SDK client created on first tool call\n- **Connection testing** built-in\n- **All logging to stderr** to avoid polluting MCP stdio transport\n\n## One-Click Deployment\n\n> [!IMPORTANT]\n> **Before you click:** this server depends on `@wyre-technology/node-hudu`,\n> which is hosted on the **GitHub Packages** npm registry. GitHub Packages has no\n> anonymous access — even though the package is public, every `npm install` needs a\n> token. The cloud builder runs `npm install` for you, so you must give it one, or\n> the build fails with `npm error 401 Unauthorized ... npm.pkg.github.com`.\n>\n> 1. Create a GitHub **Personal Access Token** with the `read:packages` scope\n>    ([classic token](https://github.com/settings/tokens/new?scopes=read:packages&description=hudu-mcp%20deploy)).\n>    Any GitHub account works — you do **not** need to be a member of the\n>    `wyre-technology` org to read its public packages.\n> 2. Add it as a build variable when prompted by the deploy flow:\n>    - **DigitalOcean App Platform** → set an encrypted env var named **`NODE_AUTH_TOKEN`**\n>      with scope **Build Time** to your PAT (the `Dockerfile` reads it via\n>      `ARG NODE_AUTH_TOKEN` to authenticate `npm ci`).\n>    - **Cloudflare Workers** → set a build variable named **`NODE_AUTH_TOKEN`** to your PAT\n>      (Workers → Settings → Build → Variables and Secrets).\n\n[![Deploy to DO](https://www.deploytodo.com/do-btn-blue.svg)](https://cloud.digitalocean.com/apps/new?repo=https://github.com/WYRE-AI/hudu-mcp/tree/main)\n\n[![Deploy to Cloudflare Workers](https://deploy.workers.cloudflare.com/button)](https://deploy.workers.cloudflare.com/?url=https://github.com/WYRE-AI/hudu-mcp)\n\n> [!NOTE]\n> The DigitalOcean target builds the full Docker image and runs the complete MCP\n> server over HTTP — this is the recommended path for operators. This repo does not\n> ship a `wrangler.json`/Workers entrypoint, so for a self-hosted server prefer\n> DigitalOcean or the prebuilt container image (`ghcr.io/wyre-ai/hudu-mcp`).\n\n## Installation\n\nThis project depends on `@wyre-technology/node-hudu`, published to the **GitHub\nPackages** npm registry, which requires a token even for public packages.\nAuthenticate npm once before installing:\n\n```bash\ngit clone https://github.com/WYRE-AI/hudu-mcp.git\ncd hudu-mcp\n\n# Authenticate npm to GitHub Packages (token needs the read:packages scope)\nexport NODE_AUTH_TOKEN=$(gh auth token)   # or a PAT with read:packages\n\nnpm install\nnpm run build\n```\n\nThe repo's `.npmrc` already points the `@wyre-technology` scope at GitHub Packages and\nreads the token from `NODE_AUTH_TOKEN`, so no further config is needed.\n\n## Configuration\n\n| Variable | Required | Default | Description |\n|---|---|---|---|\n| `HUDU_BASE_URL` | Yes | - | Your Hudu instance URL (e.g., `https://docs.example.com`) |\n| `HUDU_API_KEY` | No | - | Your Hudu API key. Setting this selects **api_key** auth mode (see below); omitting it defaults to **oauth** mode. |\n| `HUDU_AUTH_MODE` | No | auto-detected | Explicitly force `api_key` or `oauth`. Overrides the auto-detection below. |\n| `MCP_TRANSPORT` | No | `stdio` | Transport type: `stdio` or `http` |\n| `MCP_HTTP_PORT` | No | `8080` | HTTP server port (when using `http` transport) |\n| `MCP_HTTP_HOST` | No | `0.0.0.0` | HTTP server host |\n| `MCP_SERVER_NAME` | No | `hudu-mcp` | Server name reported to MCP clients |\n| `MCP_SERVER_VERSION` | No | `1.0.0` | Server version reported to MCP clients |\n| `LOG_LEVEL` | No | `info` | Log level: `error`, `warn`, `info`, `debug` |\n| `LOG_FORMAT` | No | `simple` | Log format: `json` or `simple` |\n\n### Authentication modes\n\nThis server talks to Hudu in one of two ways. **`HUDU_AUTH_MODE`** picks between\nthem; if it's unset, the mode is **auto-detected** from whether `HUDU_API_KEY`\nis set:\n\n- **`api_key`** (auto-selected when `HUDU_API_KEY` is set) — the classic mode.\n  This server calls Hudu's REST API directly via `@wyre-technology/node-hudu` and\n  implements all 39 tools itself, exactly as before. This is the mode every\n  existing deployment already uses, and it is 100% unchanged and unaffected by\n  everything below.\n\n- **`oauth`** (auto-selected when `HUDU_API_KEY` is **not** set) — for newer Hudu\n  instances that expose their own native MCP server (Hudu Admin -> External Apps\n  -> MCP), protected by interactive OAuth rather than a static API key. In this\n  mode `hudu-mcp` does not reimplement any tools; it acts as a thin, authenticated\n  proxy that forwards MCP requests straight through to `{HUDU_BASE_URL}/mcp` and\n  relays the responses back, so you automatically get whatever tool surface that\n  Hudu instance exposes.\n\n  The OAuth flow (RFC 9728/8414 discovery, RFC 7591 Dynamic Client Registration,\n  PKCE `authorization_code`, no client secret — this is a public client) runs the\n  first time a request needs a token:\n\n  1. `hudu-mcp` discovers the instance's OAuth metadata from\n     `{HUDU_BASE_URL}/.well-known/oauth-protected-resource/mcp` and\n     `{HUDU_BASE_URL}/.well-known/oauth-authorization-server`.\n  2. It registers itself as a public OAuth client via Dynamic Client\n     Registration (once per Hudu instance — the issued `client_id` is cached).\n  3. It prints an authorization URL to **stderr**. Open it in a browser and\n     approve access.\n  4. A short-lived local server on `http://127.0.0.1:<ephemeral-port>/callback`\n     catches the redirect and exchanges the code for tokens.\n  5. Tokens are cached at `~/.hudu-mcp/credentials-<hash-of-base-url>.json`\n     (file mode `0600`), and transparently refreshed on later runs — you should\n     only see the browser prompt again if the refresh token itself expires or\n     is revoked.\n\n  Because this flow needs a browser that can reach a callback server bound to\n  `127.0.0.1` on the machine running `hudu-mcp`, it works for `stdio` (always\n  local to the calling MCP client) and for a self-hosted `http` transport\n  **running on the same machine you're browsing from** — e.g. local testing on\n  `localhost:8080`. It does not work for an `http` transport deployed to a\n  remote host you don't have browser-level access to (a cloud VM, a\n  container, etc.): your browser can't reach that machine's own loopback\n  interface to complete the redirect, so the first-run authorization would\n  hang. This is unrelated to gateway mode (`AUTH_MODE=gateway`), which is a\n  stateless multi-tenant proxy with credentials injected per request via\n  headers and has no single user to run a browser flow for at all — setting\n  `HUDU_AUTH_MODE=oauth` together with `AUTH_MODE=gateway` is a startup error.\n\n## Usage\n\n### Claude Desktop (stdio)\n\nAdd to your `claude_desktop_config.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"hudu\": {\n      \"command\": \"node\",\n      \"args\": [\"/path/to/hudu-mcp/dist/entry.js\"],\n      \"env\": {\n        \"HUDU_BASE_URL\": \"https://docs.example.com\",\n        \"HUDU_API_KEY\": \"your-api-key\"\n      }\n    }\n  }\n}\n```\n\nFor a newer Hudu instance with its own native MCP server (see\n[Authentication modes](#authentication-modes)), omit `HUDU_API_KEY` and only set\n`HUDU_BASE_URL` — `hudu-mcp` will print an authorization URL to the terminal the\nfirst time it's launched:\n\n```json\n{\n  \"mcpServers\": {\n    \"hudu\": {\n      \"command\": \"node\",\n      \"args\": [\"/path/to/hudu-mcp/dist/entry.js\"],\n      \"env\": {\n        \"HUDU_BASE_URL\": \"https://docs.example.com\"\n      }\n    }\n  }\n}\n```\n\n### HTTP Transport\n\n```bash\nHUDU_BASE_URL=https://docs.example.com \\\nHUDU_API_KEY=your-api-key \\\nMCP_TRANSPORT=http \\\nMCP_HTTP_PORT=8080 \\\nnpm start\n```\n\n## Tools (39)\n\n### Companies (8 tools)\n\n| Tool | Description |\n|---|---|\n| `hudu_list_companies` | List companies with optional filters |\n| `hudu_get_company` | Get a company by ID |\n| `hudu_create_company` | Create a new company |\n| `hudu_update_company` | Update an existing company |\n| `hudu_delete_company` | Delete a company |\n| `hudu_archive_company` | Archive a company |\n| `hudu_unarchive_company` | Unarchive a company |\n| `hudu_test_connection` | Test the connection to Hudu API |\n\n### Assets (6 tools)\n\n| Tool | Description |\n|---|---|\n| `hudu_list_assets` | List assets with optional filters |\n| `hudu_get_asset` | Get an asset by ID |\n| `hudu_create_asset` | Create a new asset |\n| `hudu_update_asset` | Update an existing asset |\n| `hudu_delete_asset` | Delete an asset |\n| `hudu_archive_asset` | Archive an asset |\n\n### Asset Layouts (4 tools)\n\n| Tool | Description |\n|---|---|\n| `hudu_list_asset_layouts` | List asset layouts |\n| `hudu_get_asset_layout` | Get an asset layout by ID |\n| `hudu_create_asset_layout` | Create a new asset layout |\n| `hudu_update_asset_layout` | Update an existing asset layout |\n\n### Asset Passwords (5 tools)\n\n| Tool | Description |\n|---|---|\n| `hudu_list_asset_passwords` | List asset passwords |\n| `hudu_get_asset_password` | Get an asset password by ID |\n| `hudu_create_asset_password` | Create a new asset password |\n| `hudu_update_asset_password` | Update an existing asset password |\n| `hudu_delete_asset_password` | Delete an asset password |\n\n### Articles (6 tools)\n\n| Tool | Description |\n|---|---|\n| `hudu_list_articles` | List knowledge base articles |\n| `hudu_get_article` | Get an article by ID |\n| `hudu_create_article` | Create a new article |\n| `hudu_update_article` | Update an existing article |\n| `hudu_delete_article` | Delete an article |\n| `hudu_archive_article` | Archive an article |\n\n### Websites (5 tools)\n\n| Tool | Description |\n|---|---|\n| `hudu_list_websites` | List monitored websites |\n| `hudu_get_website` | Get a website by ID |\n| `hudu_create_website` | Create a new website |\n| `hudu_update_website` | Update an existing website |\n| `hudu_delete_website` | Delete a website |\n\n### Other Resources (5 tools)\n\n| Tool | Description |\n|---|---|\n| `hudu_list_folders` | List folders |\n| `hudu_list_procedures` | List procedures |\n| `hudu_list_activity_logs` | List activity logs |\n| `hudu_list_relations` | List relations |\n| `hudu_list_magic_dash` | List Magic Dash items |\n\n## Resources\n\n| URI | Description |\n|---|---|\n| `hudu://companies` | List of all companies |\n| `hudu://companies/{id}` | Company details by ID |\n| `hudu://assets` | List of all assets |\n| `hudu://assets/{id}` | Asset details by ID |\n| `hudu://articles` | List of all articles |\n| `hudu://articles/{id}` | Article details by ID |\n\n## Development\n\n```bash\n# Install dependencies\nnpm install\n\n# Build\nnpm run build\n\n# Run in development mode\nnpm run dev\n\n# Clean build output\nnpm run clean\n```\n\n## License\n\n[Apache-2.0](LICENSE)\n",
  "bytes": 11461,
  "sha": "27d1b8c4e41092f6ce8ba76dd472dc6133cda5ffdf5fbb944c1850d5cbe26075",
  "repo_slug": "wyre-ai/hudu-mcp",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_wyre_ai_hudu_mcp_fe2227b0/readme"
}