@lovelybunch/mcp 1.0.51 → 1.0.53
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/dist/proposals-tool.js +3 -3
- package/dist/server.d.ts +2 -0
- package/dist/server.js +155 -0
- package/package.json +2 -2
- package/src/proposals-tool.ts +3 -3
package/dist/proposals-tool.js
CHANGED
|
@@ -10,7 +10,7 @@ export const listProposalsTool = {
|
|
|
10
10
|
properties: {
|
|
11
11
|
status: {
|
|
12
12
|
type: "string",
|
|
13
|
-
enum: ["draft", "proposed", "in-review", "approved", "merged", "rejected"],
|
|
13
|
+
enum: ["draft", "proposed", "in-review", "code-complete", "approved", "merged", "rejected"],
|
|
14
14
|
description: "Filter by proposal status"
|
|
15
15
|
},
|
|
16
16
|
priority: {
|
|
@@ -53,7 +53,7 @@ export const proposalsTool = {
|
|
|
53
53
|
properties: {
|
|
54
54
|
status: {
|
|
55
55
|
type: "string",
|
|
56
|
-
enum: ["draft", "proposed", "in-review", "approved", "merged", "rejected"],
|
|
56
|
+
enum: ["draft", "proposed", "in-review", "code-complete", "approved", "merged", "rejected"],
|
|
57
57
|
description: "Filter by proposal status"
|
|
58
58
|
},
|
|
59
59
|
author: {
|
|
@@ -156,7 +156,7 @@ export const proposalsTool = {
|
|
|
156
156
|
},
|
|
157
157
|
status: {
|
|
158
158
|
type: "string",
|
|
159
|
-
enum: ["draft", "proposed", "in-review", "approved", "merged", "rejected"],
|
|
159
|
+
enum: ["draft", "proposed", "in-review", "code-complete", "approved", "merged", "rejected"],
|
|
160
160
|
description: "Current status of the proposal"
|
|
161
161
|
},
|
|
162
162
|
metadata: {
|
package/dist/server.d.ts
ADDED
package/dist/server.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createInterface } from 'readline';
|
|
3
|
+
import { stdin as input, stdout as output } from 'process';
|
|
4
|
+
import fs from 'fs';
|
|
5
|
+
import path from 'path';
|
|
6
|
+
function send(id, result, error) {
|
|
7
|
+
const payload = { jsonrpc: '2.0', id };
|
|
8
|
+
if (error)
|
|
9
|
+
payload.error = error;
|
|
10
|
+
else
|
|
11
|
+
payload.result = result;
|
|
12
|
+
output.write(JSON.stringify(payload) + '\n');
|
|
13
|
+
}
|
|
14
|
+
function resolveNutPath() {
|
|
15
|
+
if (process.env.GAIT_DATA_PATH)
|
|
16
|
+
return path.join(process.env.GAIT_DATA_PATH, '.nut');
|
|
17
|
+
// Try to find nearest .nut folder upward from cwd
|
|
18
|
+
let dir = process.cwd();
|
|
19
|
+
for (;;) {
|
|
20
|
+
const p = path.join(dir, '.nut');
|
|
21
|
+
if (fs.existsSync(p))
|
|
22
|
+
return p;
|
|
23
|
+
const parent = path.dirname(dir);
|
|
24
|
+
if (parent === dir)
|
|
25
|
+
break;
|
|
26
|
+
dir = parent;
|
|
27
|
+
}
|
|
28
|
+
return path.join(process.cwd(), '.nut');
|
|
29
|
+
}
|
|
30
|
+
function listProposalFiles() {
|
|
31
|
+
const base = resolveNutPath();
|
|
32
|
+
const p = path.join(base, 'proposals');
|
|
33
|
+
try {
|
|
34
|
+
return fs.readdirSync(p).filter((f) => f.endsWith('.md'));
|
|
35
|
+
}
|
|
36
|
+
catch {
|
|
37
|
+
return [];
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
function readProposal(id) {
|
|
41
|
+
const base = resolveNutPath();
|
|
42
|
+
const fp = path.join(base, 'proposals', `${id}.md`);
|
|
43
|
+
try {
|
|
44
|
+
const content = fs.readFileSync(fp, 'utf-8');
|
|
45
|
+
// Naive frontmatter parse (best effort, no YAML dependency)
|
|
46
|
+
let frontmatter;
|
|
47
|
+
if (content.startsWith('---')) {
|
|
48
|
+
const end = content.indexOf('\n---', 3);
|
|
49
|
+
if (end > 0) {
|
|
50
|
+
const fm = content.slice(3, end).split('\n');
|
|
51
|
+
frontmatter = {};
|
|
52
|
+
for (const line of fm) {
|
|
53
|
+
const m = /^([A-Za-z0-9_\-]+):\s*(.*)$/.exec(line.trim());
|
|
54
|
+
if (m)
|
|
55
|
+
frontmatter[m[1]] = m[2];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return { content, frontmatter };
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
const tools = [
|
|
66
|
+
{
|
|
67
|
+
name: 'list_proposals',
|
|
68
|
+
description: 'List all change proposals with basic metadata',
|
|
69
|
+
input_schema: {
|
|
70
|
+
type: 'object',
|
|
71
|
+
properties: {
|
|
72
|
+
filters: { type: 'object' },
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: 'change_proposals',
|
|
78
|
+
description: 'Manage change proposals (list, get)',
|
|
79
|
+
input_schema: {
|
|
80
|
+
type: 'object',
|
|
81
|
+
properties: {
|
|
82
|
+
operation: { type: 'string', enum: ['list', 'get'] },
|
|
83
|
+
id: { type: 'string' },
|
|
84
|
+
filters: { type: 'object' },
|
|
85
|
+
},
|
|
86
|
+
required: ['operation'],
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
];
|
|
90
|
+
async function handleCall(params) {
|
|
91
|
+
const name = params?.name || params?.tool?.name;
|
|
92
|
+
const args = params?.arguments || params?.tool?.input || {};
|
|
93
|
+
switch (name) {
|
|
94
|
+
case 'list_proposals': {
|
|
95
|
+
const files = listProposalFiles();
|
|
96
|
+
const data = files.map((f) => ({ id: f.replace(/\.md$/, ''), title: f.replace(/\.md$/, '') }));
|
|
97
|
+
return { success: true, data, message: `Found ${data.length} proposals` };
|
|
98
|
+
}
|
|
99
|
+
case 'change_proposals': {
|
|
100
|
+
const op = args.operation;
|
|
101
|
+
if (op === 'list') {
|
|
102
|
+
const files = listProposalFiles();
|
|
103
|
+
const data = files.map((f) => ({ id: f.replace(/\.md$/, ''), title: f.replace(/\.md$/, '') }));
|
|
104
|
+
return { success: true, data, message: `Found ${data.length} proposals` };
|
|
105
|
+
}
|
|
106
|
+
if (op === 'get') {
|
|
107
|
+
if (!args.id)
|
|
108
|
+
return { success: false, error: 'Proposal ID required' };
|
|
109
|
+
const p = readProposal(args.id);
|
|
110
|
+
if (!p)
|
|
111
|
+
return { success: false, error: 'Proposal not found' };
|
|
112
|
+
return { success: true, data: { id: args.id, content: p.content, frontmatter: p.frontmatter || {} } };
|
|
113
|
+
}
|
|
114
|
+
return { success: false, error: 'Operation not implemented' };
|
|
115
|
+
}
|
|
116
|
+
default:
|
|
117
|
+
return { success: false, error: 'Unknown tool' };
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function start() {
|
|
121
|
+
const rl = createInterface({ input });
|
|
122
|
+
rl.on('line', async (line) => {
|
|
123
|
+
let msg;
|
|
124
|
+
try {
|
|
125
|
+
msg = JSON.parse(line);
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return; // ignore invalid JSON
|
|
129
|
+
}
|
|
130
|
+
const { id, method, params } = msg || {};
|
|
131
|
+
try {
|
|
132
|
+
if (method === 'initialize') {
|
|
133
|
+
send(id, {
|
|
134
|
+
serverInfo: { name: '@lovelybunch/mcp-proposals', version: '1.0.0' },
|
|
135
|
+
capabilities: { tools: {} },
|
|
136
|
+
});
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
if (method === 'tools/list') {
|
|
140
|
+
send(id, { tools });
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
if (method === 'tools/call') {
|
|
144
|
+
const result = await handleCall(params);
|
|
145
|
+
send(id, { content: [{ type: 'text', text: JSON.stringify(result) }] });
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
send(id, undefined, { code: -32601, message: 'Method not found' });
|
|
149
|
+
}
|
|
150
|
+
catch (e) {
|
|
151
|
+
send(id, undefined, { code: -32000, message: e?.message || 'Server error' });
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
start();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lovelybunch/mcp",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.53",
|
|
4
4
|
"description": "MCP tools for Coconut",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"dev": "tsc --watch"
|
|
10
10
|
},
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@lovelybunch/types": "^1.0.
|
|
12
|
+
"@lovelybunch/types": "^1.0.53",
|
|
13
13
|
"hono": "^4.0.0"
|
|
14
14
|
},
|
|
15
15
|
"devDependencies": {
|
package/src/proposals-tool.ts
CHANGED
|
@@ -27,7 +27,7 @@ export const listProposalsTool: MCPTool = {
|
|
|
27
27
|
properties: {
|
|
28
28
|
status: {
|
|
29
29
|
type: "string",
|
|
30
|
-
enum: ["draft", "proposed", "in-review", "approved", "merged", "rejected"],
|
|
30
|
+
enum: ["draft", "proposed", "in-review", "code-complete", "approved", "merged", "rejected"],
|
|
31
31
|
description: "Filter by proposal status"
|
|
32
32
|
},
|
|
33
33
|
priority: {
|
|
@@ -71,7 +71,7 @@ export const proposalsTool: MCPTool = {
|
|
|
71
71
|
properties: {
|
|
72
72
|
status: {
|
|
73
73
|
type: "string",
|
|
74
|
-
enum: ["draft", "proposed", "in-review", "approved", "merged", "rejected"],
|
|
74
|
+
enum: ["draft", "proposed", "in-review", "code-complete", "approved", "merged", "rejected"],
|
|
75
75
|
description: "Filter by proposal status"
|
|
76
76
|
},
|
|
77
77
|
author: {
|
|
@@ -174,7 +174,7 @@ export const proposalsTool: MCPTool = {
|
|
|
174
174
|
},
|
|
175
175
|
status: {
|
|
176
176
|
type: "string",
|
|
177
|
-
enum: ["draft", "proposed", "in-review", "approved", "merged", "rejected"],
|
|
177
|
+
enum: ["draft", "proposed", "in-review", "code-complete", "approved", "merged", "rejected"],
|
|
178
178
|
description: "Current status of the proposal"
|
|
179
179
|
},
|
|
180
180
|
metadata: {
|