mcp-server-mojaq 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +51 -0
  3. package/index.js +109 -0
  4. package/package.json +42 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 MOJAQ
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,51 @@
1
+ # mcp-server-mojaq
2
+
3
+ Read-only [MCP](https://modelcontextprotocol.io) server for [MOJAQ](https://mojaq.com) — ask Claude, Claude Code or Cursor about your EU-hosted app's **deploys, errors, logs, uptime, incidents and root-cause hints**, in natural language.
4
+
5
+ Because MOJAQ keeps all of these in one place, your AI assistant can *correlate* across them — "errors spiked 4 minutes after deploy `v2.4.0` while flag `new-checkout` was on" — right inside your editor. It is **strictly read-only**: no tool can change or delete anything.
6
+
7
+ ## Setup
8
+
9
+ You need a MOJAQ API key (from [mojaq.com/app/api-keys](https://mojaq.com/app/api-keys)).
10
+
11
+ **Claude Desktop / Claude Code / Cursor** — add to your MCP config:
12
+
13
+ ```json
14
+ {
15
+ "mcpServers": {
16
+ "mojaq": {
17
+ "command": "npx",
18
+ "args": ["-y", "mcp-server-mojaq"],
19
+ "env": { "MOJAQ_API_KEY": "mq_your_key_here" }
20
+ }
21
+ }
22
+ }
23
+ ```
24
+
25
+ Then ask your assistant things like:
26
+
27
+ - *"What broke in the last hour?"*
28
+ - *"Show me the errors on /checkout and tail the payment-service logs for timeouts."*
29
+ - *"What did the last deploy change, and did error rates go up after it?"*
30
+ - *"Is anything down right now?"*
31
+
32
+ ## Tools
33
+
34
+ | Tool | What it does |
35
+ |------|--------------|
36
+ | `project_summary` | Monitors + status, open incidents, recent deploys, root-cause hints, log volume |
37
+ | `get_timeline` | One chronological feed of deploys, incidents, flag changes and hints |
38
+ | `explain_recent_regression` | Timeline for the last N hours, to reason about a recent regression |
39
+ | `query_logs` | Search logs by text, level, service and time window |
40
+ | `get_deploys` | Recent deployments |
41
+ | `uptime_status` | Uptime monitors and their status |
42
+ | `search_errors` | Recent error-tracking events |
43
+
44
+ ## Config
45
+
46
+ | Env | Required | Default |
47
+ |-----|----------|---------|
48
+ | `MOJAQ_API_KEY` | yes | — |
49
+ | `MOJAQ_API_URL` | no | `https://api.mojaq.com` |
50
+
51
+ Read-only. MIT licensed. EU-hosted (Helsinki).
package/index.js ADDED
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * MOJAQ MCP server (read-only).
4
+ *
5
+ * Exposes your MOJAQ-monitored app to an AI assistant (Claude Desktop, Claude
6
+ * Code, Cursor, ...) as a set of read-only tools. The AI can ask what broke,
7
+ * search errors and logs, look at deploys and uptime, and correlate across all
8
+ * of them — but it can never mutate anything.
9
+ *
10
+ * Config (env):
11
+ * MOJAQ_API_KEY (required) your MOJAQ API key
12
+ * MOJAQ_API_URL (optional) default https://api.mojaq.com
13
+ */
14
+ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
15
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
16
+ import { z } from 'zod';
17
+
18
+ const API_URL = (process.env.MOJAQ_API_URL || 'https://api.mojaq.com').replace(/\/+$/, '');
19
+ const API_KEY = process.env.MOJAQ_API_KEY;
20
+
21
+ if (!API_KEY) {
22
+ console.error('mcp-server-mojaq: MOJAQ_API_KEY is required. Get one at https://mojaq.com/app/api-keys');
23
+ process.exit(1);
24
+ }
25
+
26
+ async function apiGet(path, params = {}) {
27
+ const url = new URL(API_URL + path);
28
+ for (const [k, v] of Object.entries(params)) {
29
+ if (v !== undefined && v !== null && v !== '') url.searchParams.set(k, String(v));
30
+ }
31
+ const res = await fetch(url, {
32
+ headers: { 'x-api-key': API_KEY, 'user-agent': 'mcp-server-mojaq/1.0' },
33
+ signal: AbortSignal.timeout(20000),
34
+ });
35
+ const text = await res.text();
36
+ if (!res.ok) throw new Error(`MOJAQ API ${res.status}: ${text.slice(0, 300)}`);
37
+ try {
38
+ return JSON.parse(text);
39
+ } catch {
40
+ return text;
41
+ }
42
+ }
43
+
44
+ const ok = (obj) => ({ content: [{ type: 'text', text: typeof obj === 'string' ? obj : JSON.stringify(obj, null, 2) }] });
45
+ const fail = (e) => ({ content: [{ type: 'text', text: `Error: ${e.message}` }], isError: true });
46
+ const wrap = (fn) => async (args) => {
47
+ try {
48
+ return ok(await fn(args || {}));
49
+ } catch (e) {
50
+ return fail(e);
51
+ }
52
+ };
53
+
54
+ const server = new McpServer({ name: 'mojaq', version: '1.0.0' });
55
+
56
+ server.tool(
57
+ 'project_summary',
58
+ 'High-level snapshot of the MOJAQ-monitored app: uptime monitors and their status, open incidents, recent deploys, recent root-cause hints, heartbeat count and 24h log volume. Start here.',
59
+ wrap(() => apiGet('/v1/summary')),
60
+ );
61
+
62
+ server.tool(
63
+ 'get_timeline',
64
+ 'Unified chronological feed of everything that happened across all tools: deploys, incidents, feature-flag changes and AI root-cause hints. Use this to answer "what changed / what happened recently".',
65
+ { since_hours: z.number().int().min(1).max(720).optional().describe('Look-back window in hours (default 168 = 7 days)'), limit: z.number().int().min(1).max(200).optional() },
66
+ wrap(({ since_hours, limit }) => apiGet('/v1/timeline', { since_hours, limit })),
67
+ );
68
+
69
+ server.tool(
70
+ 'explain_recent_regression',
71
+ 'Return the timeline for the last N hours (default 2) so you can reason about what likely caused a recent regression: which deploy, flag change or incident lines up with the problem.',
72
+ { since_hours: z.number().int().min(1).max(72).optional() },
73
+ wrap(({ since_hours }) => apiGet('/v1/timeline', { since_hours: since_hours ?? 2, limit: 100 })),
74
+ );
75
+
76
+ server.tool(
77
+ 'query_logs',
78
+ 'Search the app logs (EU-stored). Filter by free-text, level and service over a time window.',
79
+ {
80
+ q: z.string().optional().describe('Free-text to match in the log message'),
81
+ level: z.enum(['trace', 'debug', 'info', 'warn', 'error', 'fatal']).optional(),
82
+ service: z.string().optional(),
83
+ since_minutes: z.number().int().min(1).max(10080).optional().describe('Look-back in minutes (default 60)'),
84
+ limit: z.number().int().min(1).max(500).optional(),
85
+ },
86
+ wrap(({ q, level, service, since_minutes, limit }) => apiGet('/v1/logs/query', { q, level, service, since_minutes, limit })),
87
+ );
88
+
89
+ server.tool(
90
+ 'get_deploys',
91
+ 'List recent deployments recorded in MOJAQ (version, environment, commit, time).',
92
+ wrap(() => apiGet('/v1/deploys')),
93
+ );
94
+
95
+ server.tool(
96
+ 'uptime_status',
97
+ 'List the uptime monitors and their current status.',
98
+ wrap(() => apiGet('/v1/monitors')),
99
+ );
100
+
101
+ server.tool(
102
+ 'search_errors',
103
+ 'List recent error-tracking events (Sentry-compatible) for the app.',
104
+ wrap(() => apiGet('/v1/errors/events')),
105
+ );
106
+
107
+ const transport = new StdioServerTransport();
108
+ await server.connect(transport);
109
+ console.error('mcp-server-mojaq: ready (read-only) against ' + API_URL);
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "mcp-server-mojaq",
3
+ "version": "1.0.0",
4
+ "description": "Read-only MCP server for MOJAQ \u2014 ask Claude, Claude Code or Cursor about your EU-hosted app's deploys, errors, logs, uptime, analytics and incidents.",
5
+ "type": "module",
6
+ "bin": {
7
+ "mcp-server-mojaq": "index.js"
8
+ },
9
+ "main": "index.js",
10
+ "files": [
11
+ "index.js",
12
+ "README.md"
13
+ ],
14
+ "engines": {
15
+ "node": ">=18"
16
+ },
17
+ "keywords": [
18
+ "mcp",
19
+ "modelcontextprotocol",
20
+ "claude",
21
+ "cursor",
22
+ "mojaq",
23
+ "observability",
24
+ "monitoring",
25
+ "eu",
26
+ "gdpr"
27
+ ],
28
+ "homepage": "https://mojaq.com/mcp",
29
+ "license": "MIT",
30
+ "dependencies": {
31
+ "@modelcontextprotocol/sdk": "^1.29.0",
32
+ "zod": "^3.23.0"
33
+ },
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/mojaqhq/mcp-server-mojaq.git"
37
+ },
38
+ "bugs": {
39
+ "url": "https://github.com/mojaqhq/mcp-server-mojaq/issues"
40
+ },
41
+ "author": "MOJAQ"
42
+ }