{
  "markdown": "<div align=\"center\">\n  <img src=\"https://raw.githubusercontent.com/brbousnguar/mulewatch/main/docs/logo.png\" width=\"72\" alt=\"mulewatch\" />\n  <h1>mulewatch</h1>\n  <p><strong>Read-only observability for MuleSoft Anypoint Platform, over MCP.</strong><br/>Application logs, historical archive search, Runtime Manager, API Manager and Exchange — for any Anypoint customer, using their own credentials.</p>\n\n  <p>\n    <img src=\"https://img.shields.io/badge/platform-stdio-174f3d?style=flat-square\" alt=\"Platform\" />\n    <img src=\"https://img.shields.io/badge/MCP%20SDK-1.30-5b21b6?style=flat-square\" alt=\"MCP SDK\" />\n    <img src=\"https://img.shields.io/badge/Node.js-20%2B-339933?style=flat-square&logo=node.js&logoColor=white\" alt=\"Node.js\" />\n    <img src=\"https://img.shields.io/badge/MuleSoft-Anypoint%20Platform-00A2DF?style=flat-square\" alt=\"MuleSoft Anypoint\" />\n    <img src=\"https://img.shields.io/npm/v/mulewatch?style=flat-square&color=cb3837&logo=npm&logoColor=white\" alt=\"npm\" />\n    <img src=\"https://img.shields.io/badge/license-Apache--2.0-blue?style=flat-square\" alt=\"License\" />\n  </p>\n</div>\n\n---\n\n## What it does\n\nmulewatch bridges an MCP client — Claude Desktop, Claude Code, Cursor, OpenClaw — to MuleSoft Anypoint Platform, so you can ask about a Mule estate in plain language and get real answers from the platform APIs. It is aimed at the person holding the pager, not the person writing the flow: no local Mule project is required, and **every tool is read-only**.\n\n- **Application logs** from Runtime Manager, normalized across CloudHub 2.0, Runtime Fabric and legacy CloudHub.\n- **Historical log search** through the Anypoint Monitoring Archive API, reaching incidents long past the live tail's rolling buffer.\n- **Analysis-ready log payloads** — normalized lines plus counts by priority, logger and replica.\n- **Runtime Manager inventory** across Application Manager, CloudHub and hybrid on-prem targets.\n- **API Manager** instances and **Exchange** assets, so a single server answers \"what is deployed, what is it fronted by, and what broke\".\n\nNothing is hardcoded to one customer: business groups, environments and credentials all come from your own connected app.\n\n### How this relates to the official MuleSoft MCP server\n\nMuleSoft publishes its own MCP server ([`mulesoft-mcp-server`](https://www.npmjs.com/package/mulesoft-mcp-server), [docs](https://docs.mulesoft.com/mulesoft-mcp-server)). It is a **development** tool — it scaffolds Mule projects, generates flows and API specs, and deploys applications, largely from a local project in an IDE.\n\nmulewatch is an **operations** tool. It never writes to your estate, it assumes no local project, and it covers the one area the official server does not address at all: **retrieving and searching application logs**, including historical logs from the Monitoring Archive. The two run happily side by side, and if you want deployment or scaffolding tools, use the official one.\n\n## Requirements\n\n- Node.js 20+\n- An Anypoint Platform account with access to the business group you want to read\n- A **connected app** (recommended) with *\"acts on its own behalf\"* enabled, or platform credentials\n- For historical log search only: **Anypoint Monitoring enabled** for the organization\n\n### Connected-app scopes\n\nGrant the scopes for the surfaces you intend to use, on the business groups and environments you care about. Anypoint returns a bare `403 Forbidden` when a scope is missing; mulewatch catches that and names the scope you probably need.\n\n| Surface | Scope |\n|---|---|\n| Environments, business groups | `View Environments`, `View Organization` |\n| Runtime Manager apps and logs | `Read Applications` |\n| Historical archive search | Anypoint Monitoring `Viewer` |\n| API Manager instances | `View APIs Configuration` |\n| Exchange assets | Exchange viewer access |\n\n## Setup\n\nCreate a connected app in **Anypoint Platform → Access Management → Connected Apps**, choose *\"App acts on its own behalf (client credentials)\"*, grant the scopes above, and copy the client id and secret.\n\nConfiguration is environment variables only — there is no config file and no organization default.\n\n```dotenv\nANYPOINT_CLIENT_ID=your_connected_app_client_id\nANYPOINT_CLIENT_SECRET=your_connected_app_client_secret\n```\n\n| Variable | Required | Purpose |\n|---|---|---|\n| `ANYPOINT_CLIENT_ID` / `ANYPOINT_CLIENT_SECRET` | one auth method | Connected app client credentials, preferred for automation |\n| `ANYPOINT_USERNAME` / `ANYPOINT_PASSWORD` | one auth method | Platform login |\n| `ANYPOINT_ACCESS_TOKEN` | one auth method | Pre-generated bearer token; takes precedence over the other two |\n| `ANYPOINT_ORG_ID` | no | Business group id. Unset means \"the organization the credentials belong to\" |\n| `ANYPOINT_ALLOWED_ENVIRONMENTS` | no | Comma-separated environment names or ids; when set, every tool refuses anything outside the list |\n| `ANYPOINT_BASE_URL` | no | Control plane host, defaults to `https://anypoint.mulesoft.com` |\n| `ANYPOINT_MONITORING_BASE_URL` | no | Monitoring host, defaults to `https://monitoring.anypoint.mulesoft.com` |\n\n### Business groups\n\nEnvironments live on **business groups**, not on the root organization. If `anypoint_list_environments` comes back empty, your credentials resolved to the root org — call `anypoint_list_business_groups` to list the ids, then pass one as `orgId` or pin it with `ANYPOINT_ORG_ID`.\n\n### Keeping production out of reach\n\n`ANYPOINT_ALLOWED_ENVIRONMENTS` scopes the whole server, not one tool. With\n\n```dotenv\nANYPOINT_ALLOWED_ENVIRONMENTS=Dev,Sandbox\n```\n\nevery tool refuses an environment outside the list and says why, whichever business group is targeted. Combine it with a connected app that only has access to those environments for defence in depth.\n\n## Run\n\nNothing to install — point your MCP client at `npx`:\n\n```json\n{\n  \"mcpServers\": {\n    \"mulewatch\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"mulewatch\"],\n      \"env\": {\n        \"ANYPOINT_CLIENT_ID\": \"your_connected_app_client_id\",\n        \"ANYPOINT_CLIENT_SECRET\": \"your_connected_app_client_secret\"\n      }\n    }\n  }\n}\n```\n\nThat block works as-is in Claude Desktop (`claude_desktop_config.json`), Cursor (`.cursor/mcp.json`) and OpenClaw. For Claude Code:\n\n```bash\nclaude mcp add mulewatch --env ANYPOINT_CLIENT_ID=... --env ANYPOINT_CLIENT_SECRET=... -- npx -y mulewatch\n```\n\nFrom a clone instead:\n\n```bash\nnpm install\nnpm run build\nnpm start\n```\n\nThe server speaks `stdio`, which is what MCP clients expect. Start with `anypoint_whoami` to confirm credentials, business group and scope before anything else.\n\n---\n\n## Tools\n\nEvery tool is read-only. All of them accept `orgId` to override the configured business group.\n\n### `anypoint_whoami`\n\nConfirms which credentials, business group, hosts and environment allowlist the server is running with. No arguments. Call this first when anything fails.\n\n### `anypoint_list_business_groups`\n\nLists the business groups beneath the credentials' organization, with ids and nesting depth.\n\n```json\n{ \"rootOrgId\": \"optional-root-organization-id\" }\n```\n\n### `anypoint_list_environments`\n\nLists environments for a business group, filtered by `ANYPOINT_ALLOWED_ENVIRONMENTS` when set.\n\n```json\n{ \"orgId\": \"your-business-group-id\" }\n```\n\n### `anypoint_list_deployed_apps`\n\nLists deployed Mule applications for an environment, by `environmentId` or `environmentName`.\n\n```json\n{ \"environmentName\": \"Dev\", \"target\": \"all\" }\n```\n\n| Option | Purpose |\n|---|---|\n| `target` | `application_manager` (default), `cloudhub`, `hybrid`, or `all` |\n| `limit` | Maximum records to return, default `100` |\n| `includeRaw` | Include raw API objects in the response |\n| `includeErrors` | Include per-target errors when a target endpoint is unavailable or not permitted |\n\n`application_manager` covers CloudHub 2.0 and Runtime Fabric deployments, `cloudhub` legacy CloudHub, `hybrid` on-prem Runtime Manager. `all` queries every target and reports per-target failures instead of aborting.\n\n### `anypoint_list_app_logs`\n\nLists the latest Runtime Manager logs for an application.\n\n```json\n{ \"environmentName\": \"Dev\", \"appName\": \"my-mule-app\" }\n```\n\n| Option | Purpose |\n|---|---|\n| `environmentId` | Use instead of `environmentName` |\n| `deploymentId` | Use when the app name is ambiguous or already known |\n| `specId` | A specific Application Manager deployment spec/config id |\n| `target` | `auto` (default), `application_manager`, or `cloudhub` |\n| `limit` | Maximum log entries, default `100`, max `500` |\n| `offset` | Application Manager log offset, default `0` |\n| `descending` | Latest first, default `true` |\n| `startTime` / `endTime` | Application Manager time filter, ISO 8601 |\n| `includeRaw` | Include the raw API response |\n| `includeErrors` | Include failed target errors when `target` is `auto` |\n\nFor CloudHub 2.0 and Runtime Fabric the tool resolves `appName` to a deployment, resolves the current spec, then reads that spec's logs. For legacy CloudHub it calls the CloudHub logs endpoint directly.\n\n### `anypoint_get_app_logs_for_analysis`\n\nSame sources as above, but returns an analysis-ready payload rather than a raw list. Prefer it when diagnosing something.\n\n```json\n{ \"environmentName\": \"Dev\", \"appName\": \"my-mule-app\", \"limit\": 200, \"errorOnly\": true }\n```\n\nTakes every option of `anypoint_list_app_logs`, plus:\n\n| Option | Purpose |\n|---|---|\n| `searchTerms` | Case-insensitive filters matched against timestamp, priority, message, logger, thread or instance |\n| `errorOnly` | Only return `ERROR` and `FATAL` lines |\n\nThe response carries `analysis.totalFetched`, `analysis.totalMatched`, `analysis.priorityCounts`, `analysis.loggerCounts`, `analysis.instanceCounts` and `analysis.logs`. Counts describe everything fetched; `logs` holds what matched.\n\n### `anypoint_search_archived_logs`\n\nSearches historical logs through the **Anypoint Monitoring Archive API**, for dates beyond what the live tail can see.\n\nThis is the tool the others cannot replace. `anypoint_list_app_logs` and `anypoint_get_app_logs_for_analysis` read Runtime Manager's live tail — a small rolling buffer that can scroll out within minutes on a busy application, no matter what `startTime`/`endTime` you pass. The archive lands files roughly ten minutes after each ten-minute window closes and keeps them far longer.\n\n**Requires Anypoint Monitoring to be enabled for the organization.** Without it every call 403s or 404s; fall back to a manual *Download Logs* export from the Runtime Manager UI.\n\n```json\n{\n  \"environmentName\": \"Prod\",\n  \"appName\": \"my-mule-app\",\n  \"date\": \"2026-08-09\",\n  \"searchTerms\": [\"Started product import\", \"Import finished\"]\n}\n```\n\n| Option | Purpose |\n|---|---|\n| `date` | Required, `YYYY-MM-DD` (UTC) |\n| `endDate` | Optional inclusive range end, `YYYY-MM-DD` (UTC), max 7 days total |\n| `searchTerms` | Case-insensitive text filters |\n| `errorOnly` | Only return `ERROR` and `FATAL` lines |\n| `maxEntities` | Cap on replica entities probed during a full-scan fallback, default `150`, max `300` |\n| `maxFiles` | Cap on archive files downloaded and parsed, default `100`, max `500` |\n| `limit` | Cap on matched lines returned, default `500`, max `2000` |\n| `includeRaw` | Include the raw parsed entry alongside the normalized fields |\n\n**How it finds your logs.** The Archive API indexes per replica/pod (`{appName}_{replicaId}`), not per application, so the replica has to be identified before anything can be read. mulewatch first tries the replica ids visible in the live Runtime Manager tail (`entityResolution: \"live-replica\"`) — pods are usually long-lived between redeploys, so that same replica normally produced the logs on recent past dates too, and a typical lookup costs a handful of archive calls. If that replica has no files for the requested dates — the application was redeployed since, say — it falls back to listing every replica the app has ever had and probing each (`entityResolution: \"full-scan\"`), bounded by `maxEntities`.\n\nThe Archive API allows 60 requests/minute; the client throttles beneath that and backs off on `429`. A full-scan fallback across many replicas can take a while, so narrow the date range where you can. The response reports which path was taken and whether any cap truncated the result.\n\n### `anypoint_list_api_manager_instances`\n\nLists API instances managed by API Manager in an environment.\n\n```json\n{ \"environmentName\": \"Dev\", \"assetId\": \"my-api\" }\n```\n\n| Option | Purpose |\n|---|---|\n| `assetId` | Filter by Exchange asset id |\n| `technology` | Filter by technology, e.g. `mule4`, `flexGateway` |\n| `limit` / `offset` | Pagination, default `100` / `0` |\n| `includeRaw` | Include raw API objects |\n\n### `anypoint_search_exchange_assets`\n\nSearches Anypoint Exchange for assets in the organization.\n\n```json\n{ \"search\": \"customer\", \"types\": [\"rest-api\"], \"limit\": 20 }\n```\n\n| Option | Purpose |\n|---|---|\n| `search` | Free-text search over asset names and descriptions |\n| `types` | Filter by asset type, e.g. `rest-api`, `connector`, `template`, `example` |\n| `limit` / `offset` | Pagination, default `50` / `0` |\n| `includeRaw` | Include raw API objects |\n\n---\n\n## Tech stack\n\n| Layer | Technology |\n|---|---|\n| Runtime | Node.js 20+, ES modules |\n| Language | TypeScript 5.7, `strict`, compiled to `dist/` |\n| Protocol | Model Context Protocol SDK 1.30, `stdio` transport |\n| Validation | zod 4 |\n| Auth | Anypoint `client_credentials` token flow, username/password, or bearer token |\n| Tests | `node:test`, no runner dependency |\n\n## Repository layout\n\n```text\n.\n├── src/\n│   ├── index.ts          # bin entry: config check, stdio wiring\n│   ├── tools.ts          # MCP tool registration and argument schemas\n│   ├── client.ts         # AnypointClient: auth, org resolution, all API calls\n│   ├── config.ts         # env-var config, environment allowlist\n│   ├── normalize.ts      # envelope unwrapping, log normalization, analysis\n│   ├── archive-parse.ts  # archive log line parsing, date range enumeration\n│   └── errors.ts         # AnypointApiError and message formatting\n├── test/                 # unit tests for the pure logic\n├── docs/logo.png         # project mark, used in this README\n├── server.json           # MCP registry manifest\n└── .env.example\n```\n\n## Verification status\n\nRead paths were exercised against a live Anypoint organization during development:\n\n| Tool | Status |\n|---|---|\n| `anypoint_whoami` | Verified live |\n| `anypoint_list_business_groups` | Verified live |\n| `anypoint_list_environments` | Verified live |\n| `anypoint_list_deployed_apps` | Verified live (`application_manager`; `cloudhub`/`hybrid` targets not exercised) |\n| `anypoint_list_app_logs` | Verified live (Application Manager path) |\n| `anypoint_get_app_logs_for_analysis` | Verified live (Application Manager path) |\n| `anypoint_search_exchange_assets` | Verified live |\n| `anypoint_search_archived_logs` | Verified live — both the `live-replica` fast path and the `full-scan` fallback |\n| `anypoint_list_api_manager_instances` | **Not verified** — the test connected app lacked the `View APIs Configuration` scope and the call returned `403` |\n\nOn the application used for that check, the archive held **669 historical replica entities**. Searching the current day resolved through the live-replica fast path and probed **one** of them, finding 127 archive files and parsing 2,859 log lines. Searching a date from before the last redeploy correctly fell through to the full-scan path, which is the slow case the `maxEntities` cap exists to bound.\n\n## Notes\n\n- Every tool is read-only. There are no deploy, stop, restart or policy-mutation tools, by design — an LLM should not be mutating a production Mule estate, and the official MuleSoft server already covers deployment.\n- Tokens are cached in memory until a minute before expiry and never written to disk.\n- Credentials are read from the environment only; nothing is logged.\n- Anypoint returns bare `403`s for missing connected-app scopes. Those are caught and annotated with the scope that is probably missing.\n- EU and other control planes work by overriding `ANYPOINT_BASE_URL` and `ANYPOINT_MONITORING_BASE_URL`.\n\n## License\n\nApache-2.0. MuleSoft, Anypoint Platform and CloudHub are trademarks of Salesforce, Inc.; this is an independent project and is not affiliated with or endorsed by Salesforce.\n\n---\n\n<div align=\"center\">\n  <sub>Built by <a href=\"https://github.com/brbousnguar\">Brahim Bousnguar</a>, a MuleSoft integration engineer — available for platform and agentic-AI consulting.</sub>\n</div>\n",
  "bytes": 16552,
  "sha": "d2f7f75bdec2b951ecf54bf644dfdd4176a85093955c3e6fc90c2f51602f1b01",
  "repo_slug": "brbousnguar/mulewatch",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_brbousnguar_mulewatch_3be55c69/readme"
}