@extn/segi-mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (4) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +46 -0
  3. package/dist/index.js +265 -0
  4. package/package.json +30 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Extension Co., Ltd.
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,46 @@
1
+ # @extn/segi-mcp
2
+
3
+ Read-only MCP server for the **segi** error-monitoring platform. Connect
4
+ Claude (Code / Desktop) or any MCP client to your segi organization and let
5
+ the agent read issues, events, and stack traces — then diagnose and fix the
6
+ bugs in your own codebase.
7
+
8
+ ## Setup
9
+
10
+ 1. In the segi console, go to **Organization → API keys** and create a key.
11
+ The plaintext key (`segi_sk_live_…`) is shown **once** — copy it.
12
+ 2. Add the server to Claude Code:
13
+
14
+ ```bash
15
+ claude mcp add segi -e SEGI_SECRET_KEY=segi_sk_live_xxx -- npx -y @extn/segi-mcp
16
+ ```
17
+
18
+ Environment variables:
19
+
20
+ | Variable | Required | Description |
21
+ |---|---|---|
22
+ | `SEGI_SECRET_KEY` | yes | Organization API key from the segi console |
23
+ | `SEGI_API_URL` | no | API base URL (default `https://segiapi.extn.ai`) |
24
+
25
+ ## Tools
26
+
27
+ | Tool | Purpose |
28
+ |---|---|
29
+ | `list_projects` | Projects the key can read (call first to get project ids) |
30
+ | `get_overview` | Org-wide health: per-project counts, top unresolved issues, sparklines |
31
+ | `list_issues` | Search/filter a project's issues (status, level, text search, time range) |
32
+ | `get_issue` | Issue detail + latest event with full stack trace |
33
+ | `get_issue_events` | Paginated occurrences of one issue |
34
+ | `search_events` | Search the raw event stream |
35
+ | `get_event` | Full detail of a single event by eventId |
36
+ | `get_project_stats` | Project dashboard summary + 24h timeseries |
37
+
38
+ All tools are read-only. Keys are org-scoped (optionally restricted to a
39
+ single project) and rate-limited per minute; revoke them any time from the
40
+ console — revocation is immediate.
41
+
42
+ ## Example prompts
43
+
44
+ - "segi에서 지금 제일 시끄러운 에러가 뭐야? 원인 찾아서 고쳐줘."
45
+ - "What errors spiked after yesterday's deploy? Show me the stack traces."
46
+ - "Fix the top unresolved FATAL issue in the api project."
package/dist/index.js ADDED
@@ -0,0 +1,265 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import {
7
+ CallToolRequestSchema,
8
+ ListToolsRequestSchema
9
+ } from "@modelcontextprotocol/sdk/types.js";
10
+ var API_BASE = (process.env.SEGI_API_URL ?? "https://segiapi.extn.ai").replace(
11
+ /\/$/,
12
+ ""
13
+ );
14
+ var SECRET_KEY = process.env.SEGI_SECRET_KEY;
15
+ if (!SECRET_KEY) {
16
+ console.error(
17
+ "segi-mcp: SEGI_SECRET_KEY is not set. Create an organization API key in the segi console (Organization \u2192 API keys) and pass it via the SEGI_SECRET_KEY environment variable."
18
+ );
19
+ process.exit(1);
20
+ }
21
+ async function apiGet(path, params) {
22
+ const url = new URL(`${API_BASE}${path}`);
23
+ for (const [k, v] of Object.entries(params ?? {})) {
24
+ if (v !== void 0 && v !== "") url.searchParams.set(k, String(v));
25
+ }
26
+ const res = await fetch(url, {
27
+ headers: { authorization: `Bearer ${SECRET_KEY}` }
28
+ });
29
+ if (!res.ok) {
30
+ const body = await res.text().catch(() => "");
31
+ throw new Error(
32
+ `segi API ${res.status} ${res.statusText} for ${path}: ${body.slice(0, 500)}`
33
+ );
34
+ }
35
+ return res.json();
36
+ }
37
+ function textResult(payload) {
38
+ return {
39
+ content: [
40
+ {
41
+ type: "text",
42
+ text: typeof payload === "string" ? payload : JSON.stringify(payload, null, 2)
43
+ }
44
+ ]
45
+ };
46
+ }
47
+ function errorResult(err) {
48
+ return {
49
+ content: [
50
+ {
51
+ type: "text",
52
+ text: err instanceof Error ? err.message : String(err)
53
+ }
54
+ ],
55
+ isError: true
56
+ };
57
+ }
58
+ var PAGING_PROPS = {
59
+ page: { type: "number", description: "1-based page number. Default 1." },
60
+ pageSize: {
61
+ type: "number",
62
+ description: "Items per page (max 100). Default 20."
63
+ }
64
+ };
65
+ var server = new Server(
66
+ { name: "segi-mcp", version: "0.1.0" },
67
+ { capabilities: { tools: {} } }
68
+ );
69
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
70
+ tools: [
71
+ {
72
+ name: "list_projects",
73
+ description: "List the segi projects this API key can read (id, name, slug, platform). Call this first to discover project ids \u2014 every per-project tool needs one.",
74
+ inputSchema: { type: "object", properties: {} },
75
+ annotations: { readOnlyHint: true }
76
+ },
77
+ {
78
+ name: "get_overview",
79
+ description: "Organization-wide health overview: per-project event counts (today / 7d), unresolved issue counts by severity, the loudest unresolved issues across all projects, and 24h activity sparklines. Use this to answer 'how is my service doing?' and to decide which project/issue to drill into next.",
80
+ inputSchema: { type: "object", properties: {} },
81
+ annotations: { readOnlyHint: true }
82
+ },
83
+ {
84
+ name: "list_issues",
85
+ description: "Search a project's issues (grouped errors). Filter by status (UNRESOLVED/RESOLVED/IGNORED), level (DEBUG/INFO/WARNING/ERROR/FATAL), environment, time range, or free-text search over title/message/culprit. Sorted by most-recently-seen. Each item has eventCount/userCount so you can prioritize. Follow up with get_issue for the stack trace.",
86
+ inputSchema: {
87
+ type: "object",
88
+ properties: {
89
+ projectId: { type: "string", description: "Project id from list_projects." },
90
+ status: { type: "string", enum: ["UNRESOLVED", "RESOLVED", "IGNORED"] },
91
+ level: {
92
+ type: "string",
93
+ enum: ["DEBUG", "INFO", "WARNING", "ERROR", "FATAL"]
94
+ },
95
+ environment: { type: "string", description: "e.g. production, staging" },
96
+ search: {
97
+ type: "string",
98
+ description: "Substring match on issue title / message / culprit."
99
+ },
100
+ from: { type: "string", description: "ISO-8601 lower bound on lastSeenAt." },
101
+ to: { type: "string", description: "ISO-8601 upper bound on lastSeenAt." },
102
+ ...PAGING_PROPS
103
+ },
104
+ required: ["projectId"]
105
+ },
106
+ annotations: { readOnlyHint: true }
107
+ },
108
+ {
109
+ name: "get_issue",
110
+ description: "Full detail of one issue plus its most recent event \u2014 including the stack trace, error name, culprit (file/function), runtime, release, and breadcrumbs. This is usually all you need to locate the buggy code and fix it. Use get_issue_events if you need more occurrences to compare.",
111
+ inputSchema: {
112
+ type: "object",
113
+ properties: {
114
+ projectId: { type: "string" },
115
+ issueId: { type: "string", description: "Issue id from list_issues." }
116
+ },
117
+ required: ["projectId", "issueId"]
118
+ },
119
+ annotations: { readOnlyHint: true }
120
+ },
121
+ {
122
+ name: "get_issue_events",
123
+ description: "Paginated raw events (occurrences) of one issue, newest first, with stack traces and context. Compare several occurrences to spot patterns (same browser? same release? same user flow?) when one sample isn't conclusive.",
124
+ inputSchema: {
125
+ type: "object",
126
+ properties: {
127
+ projectId: { type: "string" },
128
+ issueId: { type: "string" },
129
+ ...PAGING_PROPS
130
+ },
131
+ required: ["projectId", "issueId"]
132
+ },
133
+ annotations: { readOnlyHint: true }
134
+ },
135
+ {
136
+ name: "search_events",
137
+ description: "Search a project's raw event stream (not grouped into issues). Filter by level, environment, release, transaction, time range, or free-text search over message/errorName/transaction. Returns a light projection; fetch one event with get_event for the full stack trace.",
138
+ inputSchema: {
139
+ type: "object",
140
+ properties: {
141
+ projectId: { type: "string" },
142
+ level: {
143
+ type: "string",
144
+ enum: ["DEBUG", "INFO", "WARNING", "ERROR", "FATAL"]
145
+ },
146
+ environment: { type: "string" },
147
+ release: { type: "string" },
148
+ transaction: { type: "string", description: "Route/transaction name." },
149
+ search: {
150
+ type: "string",
151
+ description: "Substring match on message / errorName / transaction."
152
+ },
153
+ from: { type: "string", description: "ISO-8601 lower bound on receivedAt." },
154
+ to: { type: "string", description: "ISO-8601 upper bound on receivedAt." },
155
+ ...PAGING_PROPS
156
+ },
157
+ required: ["projectId"]
158
+ },
159
+ annotations: { readOnlyHint: true }
160
+ },
161
+ {
162
+ name: "get_event",
163
+ description: "Full detail of a single event by its eventId (the string id, e.g. from search_events items): stack trace, tags, extra context, breadcrumbs, request info, device/os/browser.",
164
+ inputSchema: {
165
+ type: "object",
166
+ properties: {
167
+ projectId: { type: "string" },
168
+ eventId: {
169
+ type: "string",
170
+ description: "The event's eventId string (not the numeric row id)."
171
+ }
172
+ },
173
+ required: ["projectId", "eventId"]
174
+ },
175
+ annotations: { readOnlyHint: true }
176
+ },
177
+ {
178
+ name: "get_project_stats",
179
+ description: "One project's dashboard stats: events today/7d, unresolved issue counts by level, top unresolved issues, 10 most recent events, and a 24h per-minute timeseries by level. Use to check a project's health or verify an error stopped after a fix was deployed.",
180
+ inputSchema: {
181
+ type: "object",
182
+ properties: {
183
+ projectId: { type: "string" }
184
+ },
185
+ required: ["projectId"]
186
+ },
187
+ annotations: { readOnlyHint: true }
188
+ }
189
+ ]
190
+ }));
191
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
192
+ const { name, arguments: rawArgs = {} } = request.params;
193
+ const args = rawArgs;
194
+ const projectId = args.projectId;
195
+ try {
196
+ switch (name) {
197
+ case "list_projects":
198
+ return textResult(await apiGet("/api/mcp/v1/projects"));
199
+ case "get_overview":
200
+ return textResult(await apiGet("/api/mcp/v1/overview"));
201
+ case "list_issues":
202
+ return textResult(
203
+ await apiGet(`/api/mcp/v1/projects/${projectId}/issues`, {
204
+ status: args.status,
205
+ level: args.level,
206
+ environment: args.environment,
207
+ search: args.search,
208
+ from: args.from,
209
+ to: args.to,
210
+ page: args.page,
211
+ pageSize: args.pageSize
212
+ })
213
+ );
214
+ case "get_issue":
215
+ return textResult(
216
+ await apiGet(
217
+ `/api/mcp/v1/projects/${projectId}/issues/${args.issueId}`
218
+ )
219
+ );
220
+ case "get_issue_events":
221
+ return textResult(
222
+ await apiGet(
223
+ `/api/mcp/v1/projects/${projectId}/issues/${args.issueId}/events`,
224
+ { page: args.page, pageSize: args.pageSize }
225
+ )
226
+ );
227
+ case "search_events":
228
+ return textResult(
229
+ await apiGet(`/api/mcp/v1/projects/${projectId}/events`, {
230
+ level: args.level,
231
+ environment: args.environment,
232
+ release: args.release,
233
+ transaction: args.transaction,
234
+ search: args.search,
235
+ from: args.from,
236
+ to: args.to,
237
+ page: args.page,
238
+ pageSize: args.pageSize
239
+ })
240
+ );
241
+ case "get_event":
242
+ return textResult(
243
+ await apiGet(
244
+ `/api/mcp/v1/projects/${projectId}/events/${args.eventId}`
245
+ )
246
+ );
247
+ case "get_project_stats": {
248
+ const [summary, timeseries] = await Promise.all([
249
+ apiGet(`/api/mcp/v1/projects/${projectId}/summary`),
250
+ apiGet(`/api/mcp/v1/projects/${projectId}/timeseries`)
251
+ ]);
252
+ return textResult({ summary, timeseries });
253
+ }
254
+ default:
255
+ return {
256
+ content: [{ type: "text", text: `unknown tool: ${name}` }],
257
+ isError: true
258
+ };
259
+ }
260
+ } catch (err) {
261
+ return errorResult(err);
262
+ }
263
+ });
264
+ var transport = new StdioServerTransport();
265
+ await server.connect(transport);
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@extn/segi-mcp",
3
+ "version": "0.1.0",
4
+ "description": "MCP server that lets an AI agent (Claude) read segi error-monitoring data — issues, events, stack traces, project stats — using an organization API key",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/index.js",
8
+ "bin": {
9
+ "segi-mcp": "./dist/index.js"
10
+ },
11
+ "files": ["dist", "README.md"],
12
+ "publishConfig": {
13
+ "access": "public"
14
+ },
15
+ "scripts": {
16
+ "build": "tsup",
17
+ "dev": "tsup --watch",
18
+ "typecheck": "tsc -p tsconfig.json --noEmit",
19
+ "clean": "rm -rf dist",
20
+ "prepublishOnly": "pnpm run clean && pnpm run build"
21
+ },
22
+ "dependencies": {
23
+ "@modelcontextprotocol/sdk": "^1.0.0"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^22.7.0",
27
+ "tsup": "^8.3.0",
28
+ "typescript": "^5.6.3"
29
+ }
30
+ }