speclint-mcp 2.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.
- package/LICENSE +21 -0
- package/README.md +118 -0
- package/dist/server.d.ts +10 -0
- package/dist/server.js +343 -0
- package/glama.json +6 -0
- package/package.json +48 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Perpetual Agility LLC
|
|
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,118 @@
|
|
|
1
|
+
# Refine Backlog MCP Server
|
|
2
|
+
|
|
3
|
+
Use Refine Backlog directly inside Claude Desktop, Cursor, or any MCP-compatible client.
|
|
4
|
+
Tell your AI to refine your backlog and it calls the API automatically — no copy-paste required.
|
|
5
|
+
|
|
6
|
+
## What it does
|
|
7
|
+
|
|
8
|
+
Exposes a single tool: `refine_backlog`
|
|
9
|
+
|
|
10
|
+
Give it a list of rough backlog items. Get back structured work items with:
|
|
11
|
+
- Clean, actionable titles
|
|
12
|
+
- Problem statements
|
|
13
|
+
- Acceptance criteria (2-4 per item)
|
|
14
|
+
- T-shirt size estimates (XS/S/M/L/XL)
|
|
15
|
+
- Priorities with rationale
|
|
16
|
+
- Tags
|
|
17
|
+
- Clarifying assumptions (when needed)
|
|
18
|
+
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
### Option 1: npx (no install)
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npx refine-backlog-mcp
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Option 2: Local build
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
cd mcp
|
|
31
|
+
npm install
|
|
32
|
+
npm run build
|
|
33
|
+
node dist/server.js
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Claude Desktop Setup
|
|
37
|
+
|
|
38
|
+
Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on Mac):
|
|
39
|
+
|
|
40
|
+
```json
|
|
41
|
+
{
|
|
42
|
+
"mcpServers": {
|
|
43
|
+
"refine-backlog": {
|
|
44
|
+
"command": "npx",
|
|
45
|
+
"args": ["refine-backlog-mcp"]
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
With a license key (Pro/Team tier):
|
|
52
|
+
|
|
53
|
+
```json
|
|
54
|
+
{
|
|
55
|
+
"mcpServers": {
|
|
56
|
+
"refine-backlog": {
|
|
57
|
+
"command": "npx",
|
|
58
|
+
"args": ["refine-backlog-mcp"],
|
|
59
|
+
"env": {
|
|
60
|
+
"REFINE_LICENSE_KEY": "your-license-key-here"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
Restart Claude Desktop. You'll see "refine_backlog" in the tools list.
|
|
68
|
+
|
|
69
|
+
## Cursor Setup
|
|
70
|
+
|
|
71
|
+
Add to your Cursor MCP config (`~/.cursor/mcp.json`):
|
|
72
|
+
|
|
73
|
+
```json
|
|
74
|
+
{
|
|
75
|
+
"mcpServers": {
|
|
76
|
+
"refine-backlog": {
|
|
77
|
+
"command": "npx",
|
|
78
|
+
"args": ["refine-backlog-mcp"]
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Usage Examples
|
|
85
|
+
|
|
86
|
+
Once configured, just talk to your AI naturally:
|
|
87
|
+
|
|
88
|
+
> "Refine these backlog items: fix login bug, add CSV export, improve dashboard load time"
|
|
89
|
+
|
|
90
|
+
> "Take these 10 stories and run them through Refine Backlog. Context: we're building a B2B SaaS for HR teams."
|
|
91
|
+
|
|
92
|
+
> "Refine this backlog item as a user story with Gherkin acceptance criteria: users need to reset their password"
|
|
93
|
+
|
|
94
|
+
## Rate Limits
|
|
95
|
+
|
|
96
|
+
| Tier | Items per request | Price |
|
|
97
|
+
|------|-------------------|-------|
|
|
98
|
+
| Free | 5 | $0 — no key needed |
|
|
99
|
+
| Pro | 25 | $9/month |
|
|
100
|
+
| Team | 50 | $29/month |
|
|
101
|
+
|
|
102
|
+
Get a license key at [refinebacklog.com/pricing](https://refinebacklog.com/pricing)
|
|
103
|
+
|
|
104
|
+
## Prefer automation over chat?
|
|
105
|
+
|
|
106
|
+
If you want to run Refine Backlog in scripts, GitHub Actions, or CI pipelines — without an LLM in the loop — use the CLI instead:
|
|
107
|
+
|
|
108
|
+
```bash
|
|
109
|
+
npx refine-backlog-cli "Fix login bug" --gherkin
|
|
110
|
+
cat backlog.txt | npx refine-backlog-cli --user-stories --format json
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
**CLI repo:** [github.com/DavidNielsen1031/refine-backlog-cli](https://github.com/DavidNielsen1031/refine-backlog-cli)
|
|
114
|
+
|
|
115
|
+
## API Reference
|
|
116
|
+
|
|
117
|
+
Full API docs: [refinebacklog.com/llms.txt](https://refinebacklog.com/llms.txt)
|
|
118
|
+
OpenAPI spec: [refinebacklog.com/openapi.yaml](https://refinebacklog.com/openapi.yaml)
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Refine Backlog MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Exposes Refine Backlog (https://refinebacklog.com) as a Model Context Protocol tool.
|
|
6
|
+
* Compatible with Claude Desktop, Cursor, and any MCP-capable client.
|
|
7
|
+
*
|
|
8
|
+
* Usage: npx refine-backlog-mcp
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Refine Backlog MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Exposes Refine Backlog (https://refinebacklog.com) as a Model Context Protocol tool.
|
|
6
|
+
* Compatible with Claude Desktop, Cursor, and any MCP-capable client.
|
|
7
|
+
*
|
|
8
|
+
* Usage: npx refine-backlog-mcp
|
|
9
|
+
*/
|
|
10
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
11
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
12
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
13
|
+
const API_BASE = "https://refinebacklog.com";
|
|
14
|
+
// License key from MCP server environment — set REFINE_BACKLOG_KEY in Claude Desktop config.
|
|
15
|
+
// This is the preferred way for MCP users with a paid subscription.
|
|
16
|
+
const ENV_LICENSE_KEY = process.env.REFINE_BACKLOG_KEY ?? null;
|
|
17
|
+
const ESTIMATE_LABELS = {
|
|
18
|
+
XS: "< 1 day",
|
|
19
|
+
S: "1–2 days",
|
|
20
|
+
M: "3–5 days",
|
|
21
|
+
L: "1–2 weeks",
|
|
22
|
+
XL: "2+ weeks",
|
|
23
|
+
};
|
|
24
|
+
function formatRefinedItems(items) {
|
|
25
|
+
return items
|
|
26
|
+
.map((item, i) => {
|
|
27
|
+
const lines = [
|
|
28
|
+
`## ${i + 1}. ${item.title}`,
|
|
29
|
+
``,
|
|
30
|
+
`**Problem:** ${item.problem}`,
|
|
31
|
+
``,
|
|
32
|
+
`**Estimate:** ${item.estimate} (${ESTIMATE_LABELS[item.estimate] ?? item.estimate})`,
|
|
33
|
+
`**Priority:** ${item.priority}`,
|
|
34
|
+
`**Tags:** ${item.tags.join(", ")}`,
|
|
35
|
+
``,
|
|
36
|
+
`**Acceptance Criteria:**`,
|
|
37
|
+
...item.acceptanceCriteria.map((ac) => `- ${ac}`),
|
|
38
|
+
];
|
|
39
|
+
if (item.assumptions && item.assumptions.length > 0) {
|
|
40
|
+
lines.push(``, `**Assumptions / Open Questions:**`);
|
|
41
|
+
item.assumptions.forEach((a) => lines.push(`- ${a}`));
|
|
42
|
+
}
|
|
43
|
+
return lines.join("\n");
|
|
44
|
+
})
|
|
45
|
+
.join("\n\n---\n\n");
|
|
46
|
+
}
|
|
47
|
+
const REFINE_TOOL = {
|
|
48
|
+
name: "refine_backlog",
|
|
49
|
+
description: "Refine messy backlog items into structured, actionable work items. " +
|
|
50
|
+
"Returns each item with a clean title, problem statement, acceptance criteria, " +
|
|
51
|
+
"T-shirt size estimate (XS/S/M/L/XL), priority with rationale, tags, and optional assumptions. " +
|
|
52
|
+
"Free tier: up to 5 items per request. Pro: 25. Team: 50.\n\n" +
|
|
53
|
+
"BEFORE calling this tool, ask the user TWO quick questions if they haven't already specified:\n" +
|
|
54
|
+
"1. Would you like titles formatted as user stories? (\"As a [user], I want [goal], so that [benefit]\")\n" +
|
|
55
|
+
"2. Would you like acceptance criteria in Gherkin format? (Given/When/Then)\n" +
|
|
56
|
+
"Set useUserStories and useGherkin accordingly based on their answers. Both default to false.\n\n" +
|
|
57
|
+
"LICENSE KEY: For unlimited requests and higher item limits, set REFINE_BACKLOG_KEY in your MCP server " +
|
|
58
|
+
"environment config (Claude Desktop → claude_desktop_config.json → env section). " +
|
|
59
|
+
"Get a key at https://refinebacklog.com/pricing",
|
|
60
|
+
inputSchema: {
|
|
61
|
+
type: "object",
|
|
62
|
+
required: ["items"],
|
|
63
|
+
properties: {
|
|
64
|
+
items: {
|
|
65
|
+
type: "array",
|
|
66
|
+
items: { type: "string" },
|
|
67
|
+
minItems: 1,
|
|
68
|
+
maxItems: 50,
|
|
69
|
+
description: "Array of raw backlog item strings to refine. " +
|
|
70
|
+
"Each string is a rough description of work to be done.",
|
|
71
|
+
},
|
|
72
|
+
context: {
|
|
73
|
+
type: "string",
|
|
74
|
+
description: "Optional project context to improve relevance. " +
|
|
75
|
+
'Example: "B2B SaaS CRM for enterprise sales teams" or "Mobile fitness app for casual runners".',
|
|
76
|
+
},
|
|
77
|
+
licenseKey: {
|
|
78
|
+
type: "string",
|
|
79
|
+
description: "Optional. Refine Backlog license key for Pro or Team tier. " +
|
|
80
|
+
"Preferred: set REFINE_BACKLOG_KEY in your MCP server env config instead of passing inline. " +
|
|
81
|
+
"Get a key at https://refinebacklog.com/pricing. Free tier (5 items, 3 req/day) works without a key.",
|
|
82
|
+
},
|
|
83
|
+
useUserStories: {
|
|
84
|
+
type: "boolean",
|
|
85
|
+
description: 'Format titles as user stories: "As a [user], I want [goal], so that [benefit]". Default: false.',
|
|
86
|
+
},
|
|
87
|
+
useGherkin: {
|
|
88
|
+
type: "boolean",
|
|
89
|
+
description: "Format acceptance criteria as Gherkin: Given/When/Then. Default: false.",
|
|
90
|
+
},
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
const PLAN_TOOL = {
|
|
95
|
+
name: "plan_sprint",
|
|
96
|
+
description: "Pack refined backlog items into an AI-native sprint execution queue. " +
|
|
97
|
+
"Returns a sprint_goal, an ordered execution_queue with parallel groups and dependency chains, " +
|
|
98
|
+
"and deferred items that didn't fit the budget.\n\n" +
|
|
99
|
+
"Ideal for: CI pipelines, GitHub Actions, AI coding agents that need a machine-ready work queue.\n\n" +
|
|
100
|
+
"Items can be plain strings or objects from refine_backlog output.\n\n" +
|
|
101
|
+
"LICENSE KEY: Pro/Team tier required for dependency mapping (parallel_group, depends_on) and deferred queue. " +
|
|
102
|
+
"Set REFINE_BACKLOG_KEY in your MCP env config. Get a key at https://refinebacklog.com/pricing",
|
|
103
|
+
inputSchema: {
|
|
104
|
+
type: "object",
|
|
105
|
+
required: ["items"],
|
|
106
|
+
properties: {
|
|
107
|
+
items: {
|
|
108
|
+
type: "array",
|
|
109
|
+
items: {},
|
|
110
|
+
minItems: 1,
|
|
111
|
+
maxItems: 50,
|
|
112
|
+
description: "Array of backlog items. Can be plain strings or objects from refine_backlog output " +
|
|
113
|
+
"(with title, estimate, priority, tags fields).",
|
|
114
|
+
},
|
|
115
|
+
budget: {
|
|
116
|
+
type: "object",
|
|
117
|
+
description: "Optional sprint budget constraints.",
|
|
118
|
+
properties: {
|
|
119
|
+
max_items: {
|
|
120
|
+
type: "integer",
|
|
121
|
+
description: "Maximum number of items to include in the sprint.",
|
|
122
|
+
},
|
|
123
|
+
time_window: {
|
|
124
|
+
type: "string",
|
|
125
|
+
description: 'Sprint duration. Examples: "1 week", "2 weeks", "1 sprint".',
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
goal_hint: {
|
|
130
|
+
type: "string",
|
|
131
|
+
description: 'Optional human direction for the sprint goal. Example: "Focus on auth stability this sprint".',
|
|
132
|
+
},
|
|
133
|
+
context: {
|
|
134
|
+
type: "string",
|
|
135
|
+
description: "Optional project context to improve sprint goal quality.",
|
|
136
|
+
},
|
|
137
|
+
licenseKey: {
|
|
138
|
+
type: "string",
|
|
139
|
+
description: "Optional. License key for Pro/Team tier (dependency mapping). " +
|
|
140
|
+
"Preferred: set REFINE_BACKLOG_KEY in MCP env config. Get a key at https://refinebacklog.com/pricing",
|
|
141
|
+
},
|
|
142
|
+
},
|
|
143
|
+
},
|
|
144
|
+
};
|
|
145
|
+
const server = new Server({
|
|
146
|
+
name: "refine-backlog",
|
|
147
|
+
version: "1.0.0",
|
|
148
|
+
}, {
|
|
149
|
+
capabilities: {
|
|
150
|
+
tools: {},
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => ({
|
|
154
|
+
tools: [REFINE_TOOL, PLAN_TOOL],
|
|
155
|
+
}));
|
|
156
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
157
|
+
if (request.params.name !== "refine_backlog" && request.params.name !== "plan_sprint") {
|
|
158
|
+
return {
|
|
159
|
+
content: [{ type: "text", text: `Unknown tool: ${request.params.name}` }],
|
|
160
|
+
isError: true,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
if (request.params.name === "plan_sprint") {
|
|
164
|
+
const planArgs = request.params.arguments;
|
|
165
|
+
if (!planArgs.items || planArgs.items.length === 0) {
|
|
166
|
+
return {
|
|
167
|
+
content: [{ type: "text", text: "Error: items array is required and must not be empty." }],
|
|
168
|
+
isError: true,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
const planHeaders = { "Content-Type": "application/json" };
|
|
172
|
+
const resolvedPlanKey = planArgs.licenseKey ?? ENV_LICENSE_KEY;
|
|
173
|
+
if (resolvedPlanKey)
|
|
174
|
+
planHeaders["x-license-key"] = resolvedPlanKey;
|
|
175
|
+
const planBody = { items: planArgs.items };
|
|
176
|
+
if (planArgs.budget)
|
|
177
|
+
planBody.budget = planArgs.budget;
|
|
178
|
+
if (planArgs.goal_hint)
|
|
179
|
+
planBody.goal_hint = planArgs.goal_hint;
|
|
180
|
+
if (planArgs.context)
|
|
181
|
+
planBody.context = planArgs.context;
|
|
182
|
+
try {
|
|
183
|
+
const planResponse = await fetch(`${API_BASE}/api/plan`, {
|
|
184
|
+
method: "POST",
|
|
185
|
+
headers: planHeaders,
|
|
186
|
+
body: JSON.stringify(planBody),
|
|
187
|
+
});
|
|
188
|
+
if (planResponse.status === 429) {
|
|
189
|
+
const b = await planResponse.json().catch(() => ({}));
|
|
190
|
+
return {
|
|
191
|
+
content: [{
|
|
192
|
+
type: "text",
|
|
193
|
+
text: `⚠️ ${b.error ?? "Rate limit reached."}\n\n👉 Upgrade at https://refinebacklog.com/pricing`,
|
|
194
|
+
}],
|
|
195
|
+
isError: true,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
if (planResponse.status === 503) {
|
|
199
|
+
return {
|
|
200
|
+
content: [{ type: "text", text: "Refine Backlog AI service is temporarily unavailable. Please try again." }],
|
|
201
|
+
isError: true,
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
if (!planResponse.ok) {
|
|
205
|
+
const b = await planResponse.json().catch(() => ({ error: "Unknown error" }));
|
|
206
|
+
return {
|
|
207
|
+
content: [{ type: "text", text: `Error from Sprint Planner API: ${b.error ?? planResponse.statusText}` }],
|
|
208
|
+
isError: true,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
const planData = await planResponse.json();
|
|
212
|
+
const lines = [
|
|
213
|
+
`## 🎯 Sprint Goal`,
|
|
214
|
+
``,
|
|
215
|
+
planData.sprint_goal,
|
|
216
|
+
``,
|
|
217
|
+
`## 📋 Execution Queue (${planData.execution_queue.length} items)`,
|
|
218
|
+
``,
|
|
219
|
+
];
|
|
220
|
+
planData.execution_queue.forEach((item, i) => {
|
|
221
|
+
const groupLabel = item.parallel_group ? ` · Group ${item.parallel_group}` : "";
|
|
222
|
+
const depsLabel = item.depends_on && item.depends_on.length > 0
|
|
223
|
+
? ` · Depends on: ${item.depends_on.join(", ")}`
|
|
224
|
+
: "";
|
|
225
|
+
const estLabel = item.estimate ? ` [${item.estimate}]` : "";
|
|
226
|
+
lines.push(`**${i + 1}. ${item.title}**${estLabel}${groupLabel}${depsLabel}`);
|
|
227
|
+
lines.push(` _${item.rationale}_`);
|
|
228
|
+
lines.push(``);
|
|
229
|
+
});
|
|
230
|
+
if (planData.deferred && planData.deferred.length > 0) {
|
|
231
|
+
lines.push(`## 🔜 Deferred (${planData.deferred.length} items)`);
|
|
232
|
+
lines.push(``);
|
|
233
|
+
planData.deferred.forEach(item => lines.push(`- ${item.title}`));
|
|
234
|
+
lines.push(``);
|
|
235
|
+
}
|
|
236
|
+
const meta = planData._meta;
|
|
237
|
+
lines.push(`---`);
|
|
238
|
+
lines.push(`*${planData.execution_queue.length} items · fit ratio: ${Math.round(planData.fit_ratio * 100)}% · ${meta.latencyMs}ms · Tier: ${meta.tier} · Cost: $${meta.costUsd.toFixed(6)}*`);
|
|
239
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
240
|
+
}
|
|
241
|
+
catch (err) {
|
|
242
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
243
|
+
return {
|
|
244
|
+
content: [{ type: "text", text: `Failed to reach Sprint Planner API: ${message}` }],
|
|
245
|
+
isError: true,
|
|
246
|
+
};
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
const args = request.params.arguments;
|
|
250
|
+
if (!args.items || args.items.length === 0) {
|
|
251
|
+
return {
|
|
252
|
+
content: [{ type: "text", text: "Error: items array is required and must not be empty." }],
|
|
253
|
+
isError: true,
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
const headers = {
|
|
257
|
+
"Content-Type": "application/json",
|
|
258
|
+
};
|
|
259
|
+
// Inline arg takes precedence; env var is the recommended approach for MCP configs
|
|
260
|
+
const resolvedKey = args.licenseKey ?? ENV_LICENSE_KEY;
|
|
261
|
+
if (resolvedKey) {
|
|
262
|
+
headers["x-license-key"] = resolvedKey;
|
|
263
|
+
}
|
|
264
|
+
const body = {
|
|
265
|
+
items: args.items,
|
|
266
|
+
};
|
|
267
|
+
if (args.context)
|
|
268
|
+
body.context = args.context;
|
|
269
|
+
if (args.useUserStories !== undefined)
|
|
270
|
+
body.useUserStories = args.useUserStories;
|
|
271
|
+
if (args.useGherkin !== undefined)
|
|
272
|
+
body.useGherkin = args.useGherkin;
|
|
273
|
+
try {
|
|
274
|
+
const response = await fetch(`${API_BASE}/api/refine`, {
|
|
275
|
+
method: "POST",
|
|
276
|
+
headers,
|
|
277
|
+
body: JSON.stringify(body),
|
|
278
|
+
});
|
|
279
|
+
if (response.status === 429) {
|
|
280
|
+
const body = await response.json().catch(() => ({}));
|
|
281
|
+
const msg = body.error ?? "Daily request limit reached on the free tier.";
|
|
282
|
+
return {
|
|
283
|
+
content: [{
|
|
284
|
+
type: "text",
|
|
285
|
+
text: `⚠️ ${msg}\n\n👉 Upgrade at https://refinebacklog.com/pricing\n\nOnce you have a key, add it to your Claude Desktop config:\n{\n "mcpServers": {\n "refine-backlog": {\n "command": "npx",\n "args": ["-y", "refine-backlog-mcp"],\n "env": { "REFINE_BACKLOG_KEY": "your-key-here" }\n }\n }\n}`,
|
|
286
|
+
}],
|
|
287
|
+
isError: true,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
if (response.status === 503) {
|
|
291
|
+
return {
|
|
292
|
+
content: [{ type: "text", text: "Refine Backlog AI service is temporarily unavailable. Please try again in a moment." }],
|
|
293
|
+
isError: true,
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
if (!response.ok) {
|
|
297
|
+
const body = await response.json().catch(() => ({ error: "Unknown error" }));
|
|
298
|
+
// Item limit exceeded — clear upgrade prompt
|
|
299
|
+
if (body.upgrade) {
|
|
300
|
+
return {
|
|
301
|
+
content: [{
|
|
302
|
+
type: "text",
|
|
303
|
+
text: `⚠️ ${body.error}\n\n👉 Upgrade at https://refinebacklog.com/pricing\n\nOnce you have a license key, pass it in your request as the \`licenseKey\` parameter.`,
|
|
304
|
+
}],
|
|
305
|
+
isError: true,
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
return {
|
|
309
|
+
content: [{ type: "text", text: `Error from Refine Backlog API: ${body.error ?? response.statusText}` }],
|
|
310
|
+
isError: true,
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
const data = await response.json();
|
|
314
|
+
const formatted = formatRefinedItems(data.items);
|
|
315
|
+
const meta = data._meta;
|
|
316
|
+
const summary = [
|
|
317
|
+
`\n\n---`,
|
|
318
|
+
`*Refined ${data.items.length} item${data.items.length !== 1 ? "s" : ""} · ` +
|
|
319
|
+
`${meta.latencyMs}ms · ` +
|
|
320
|
+
`Tier: ${meta.tier} · ` +
|
|
321
|
+
`Cost: $${meta.costUsd.toFixed(6)}*`,
|
|
322
|
+
].join("\n");
|
|
323
|
+
return {
|
|
324
|
+
content: [{ type: "text", text: formatted + summary }],
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
catch (err) {
|
|
328
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
329
|
+
return {
|
|
330
|
+
content: [{ type: "text", text: `Failed to reach Refine Backlog API: ${message}` }],
|
|
331
|
+
isError: true,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
});
|
|
335
|
+
async function main() {
|
|
336
|
+
const transport = new StdioServerTransport();
|
|
337
|
+
await server.connect(transport);
|
|
338
|
+
console.error("Refine Backlog MCP server running on stdio");
|
|
339
|
+
}
|
|
340
|
+
main().catch((err) => {
|
|
341
|
+
console.error("Fatal error:", err);
|
|
342
|
+
process.exit(1);
|
|
343
|
+
});
|
package/glama.json
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "speclint-mcp",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"description": "MCP server for Speclint — lint your tickets before agents touch them",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/server.js",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"build": "tsc",
|
|
10
|
+
"start": "node server.js",
|
|
11
|
+
"dev": "ts-node server.ts"
|
|
12
|
+
},
|
|
13
|
+
"bin": {
|
|
14
|
+
"speclint-mcp": "./dist/server.js"
|
|
15
|
+
},
|
|
16
|
+
"dependencies": {
|
|
17
|
+
"@modelcontextprotocol/sdk": "^1.0.0"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"typescript": "^5.0.0",
|
|
21
|
+
"@types/node": "^22.0.0",
|
|
22
|
+
"ts-node": "^10.9.0"
|
|
23
|
+
},
|
|
24
|
+
"author": "David Nielsen <hello@speclint.ai>",
|
|
25
|
+
"homepage": "https://speclint.ai",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "https://github.com/DavidNielsen1031/speclint.git",
|
|
29
|
+
"directory": "mcp"
|
|
30
|
+
},
|
|
31
|
+
"keywords": [
|
|
32
|
+
"mcp",
|
|
33
|
+
"spec",
|
|
34
|
+
"linting",
|
|
35
|
+
"ai-agents",
|
|
36
|
+
"coding-agents",
|
|
37
|
+
"github-action",
|
|
38
|
+
"spec-quality",
|
|
39
|
+
"ai",
|
|
40
|
+
"claude"
|
|
41
|
+
],
|
|
42
|
+
"files": [
|
|
43
|
+
"dist",
|
|
44
|
+
"README.md",
|
|
45
|
+
"LICENSE",
|
|
46
|
+
"glama.json"
|
|
47
|
+
]
|
|
48
|
+
}
|