clingo-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.
- package/README.md +81 -0
- package/dist/clingoServer.js +214 -0
- package/dist/index.js +18 -0
- package/package.json +34 -0
package/README.md
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Clingo-MCP Server
|
|
2
|
+
|
|
3
|
+
A neurosymbolic AI server combining Clingo's Answer Set Programming (ASP) solver with Model Context Protocol (MCP) for hybrid AI applications.
|
|
4
|
+
|
|
5
|
+
**Features:**
|
|
6
|
+
- Answer Set Programming solver via WebAssembly
|
|
7
|
+
- In-memory program session: build up programs incrementally
|
|
8
|
+
- Session management: save/load ASP programs to disk
|
|
9
|
+
- Six core tools:
|
|
10
|
+
- `solve`: Run an ASP program and get answer sets
|
|
11
|
+
- `addToProgram`: Add facts/rules to current session
|
|
12
|
+
- `clearProgram`: Clear the current session
|
|
13
|
+
- `getProgram`: View the current program
|
|
14
|
+
- `saveSession`: Persist program to disk
|
|
15
|
+
- `loadSession`: Restore previous sessions
|
|
16
|
+
- Type safety via Zod schema validation for all I/O
|
|
17
|
+
- WebAssembly runtime: Clingo compiled to WASM
|
|
18
|
+
|
|
19
|
+
**Integration with Cline/Roo/Copilot:**
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"mcpServers": {
|
|
23
|
+
"clingo-mcp": {
|
|
24
|
+
"command": "node",
|
|
25
|
+
"args": [
|
|
26
|
+
"clingo-mcp/dist/index.js"
|
|
27
|
+
],
|
|
28
|
+
"disabled": false,
|
|
29
|
+
"alwaysAllow": [
|
|
30
|
+
"solve",
|
|
31
|
+
"addToProgram",
|
|
32
|
+
"clearProgram",
|
|
33
|
+
"getProgram",
|
|
34
|
+
"saveSession",
|
|
35
|
+
"loadSession"
|
|
36
|
+
],
|
|
37
|
+
"timeout": 30
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Development:**
|
|
44
|
+
```bash
|
|
45
|
+
git clone https://github.com/NewJerseyStyle/clingo-mcp
|
|
46
|
+
cd clingo-mcp
|
|
47
|
+
npm install
|
|
48
|
+
npm run build
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
**Example ASP Program:**
|
|
52
|
+
```asp
|
|
53
|
+
% Define persons
|
|
54
|
+
person(alice).
|
|
55
|
+
person(bob).
|
|
56
|
+
person(carol).
|
|
57
|
+
|
|
58
|
+
% Define friendships
|
|
59
|
+
friend(alice, bob).
|
|
60
|
+
friend(bob, carol).
|
|
61
|
+
|
|
62
|
+
% Rule: friendship is symmetric
|
|
63
|
+
friend(X, Y) :- friend(Y, X).
|
|
64
|
+
|
|
65
|
+
% Rule: transitive friendship
|
|
66
|
+
indirect_friend(X, Z) :- friend(X, Y), friend(Y, Z), X != Z.
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**Example Usage:**
|
|
70
|
+
```javascript
|
|
71
|
+
// Using the solve tool
|
|
72
|
+
const result = await solve({
|
|
73
|
+
program: "a. b :- a. {c; d}.",
|
|
74
|
+
models: 0 // 0 = all models
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
---
|
|
79
|
+
|
|
80
|
+
**Acknowledgements:**
|
|
81
|
+
Built with [clingo-wasm](https://github.com/domoritz/clingo-wasm) and [MCP Protocol](https://modelcontextprotocol.io)
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2
|
+
import { CallToolRequestSchema, ListToolsRequestSchema, ToolSchema, } from "@modelcontextprotocol/sdk/types.js";
|
|
3
|
+
import { z } from "zod";
|
|
4
|
+
import { zodToJsonSchema } from "zod-to-json-schema";
|
|
5
|
+
import clingo from "clingo-wasm";
|
|
6
|
+
const ToolInputSchema = ToolSchema.shape.inputSchema;
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
/* Schemas for Clingo tools */
|
|
10
|
+
const SolveSchema = z.object({
|
|
11
|
+
program: z.string().describe("The ASP (Answer Set Programming) program to solve"),
|
|
12
|
+
models: z.number().optional().default(0).describe("Number of models to compute (0 = all models)"),
|
|
13
|
+
});
|
|
14
|
+
const AddToProgramSchema = z.object({
|
|
15
|
+
code: z.string().describe("ASP facts or rules to add to the current program"),
|
|
16
|
+
});
|
|
17
|
+
const SaveSessionSchema = z.object({
|
|
18
|
+
filename: z.string().describe("The name to save the session under"),
|
|
19
|
+
});
|
|
20
|
+
const LoadSessionSchema = z.object({
|
|
21
|
+
filename: z.string().describe("The name of the session to load"),
|
|
22
|
+
});
|
|
23
|
+
var ToolName;
|
|
24
|
+
(function (ToolName) {
|
|
25
|
+
ToolName["SOLVE"] = "solve";
|
|
26
|
+
ToolName["ADD_TO_PROGRAM"] = "addToProgram";
|
|
27
|
+
ToolName["CLEAR_PROGRAM"] = "clearProgram";
|
|
28
|
+
ToolName["GET_PROGRAM"] = "getProgram";
|
|
29
|
+
ToolName["SAVE_SESSION"] = "saveSession";
|
|
30
|
+
ToolName["LOAD_SESSION"] = "loadSession";
|
|
31
|
+
})(ToolName || (ToolName = {}));
|
|
32
|
+
// Sessions directory path relative to project root
|
|
33
|
+
const SESSIONS_DIR = './clingo-sessions';
|
|
34
|
+
export const createServer = async () => {
|
|
35
|
+
// In-memory program storage (since clingo-wasm doesn't maintain state)
|
|
36
|
+
let currentProgram = "";
|
|
37
|
+
// Ensure sessions directory exists
|
|
38
|
+
if (!fs.existsSync(SESSIONS_DIR)) {
|
|
39
|
+
fs.mkdirSync(SESSIONS_DIR, { recursive: true });
|
|
40
|
+
}
|
|
41
|
+
const server = new Server({
|
|
42
|
+
name: "clingo-mcp",
|
|
43
|
+
version: "0.1.0",
|
|
44
|
+
}, {
|
|
45
|
+
capabilities: {
|
|
46
|
+
tools: {},
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
50
|
+
const tools = [
|
|
51
|
+
{
|
|
52
|
+
name: ToolName.SOLVE,
|
|
53
|
+
description: "Solves an ASP (Answer Set Programming) program using Clingo and returns the answer sets. The program can include the current session program.",
|
|
54
|
+
inputSchema: zodToJsonSchema(SolveSchema),
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
name: ToolName.ADD_TO_PROGRAM,
|
|
58
|
+
description: "Adds ASP facts or rules to the current in-memory program session",
|
|
59
|
+
inputSchema: zodToJsonSchema(AddToProgramSchema),
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: ToolName.CLEAR_PROGRAM,
|
|
63
|
+
description: "Clears the current in-memory program session",
|
|
64
|
+
inputSchema: { type: "object", properties: {}, required: [] },
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: ToolName.GET_PROGRAM,
|
|
68
|
+
description: "Returns the current in-memory ASP program",
|
|
69
|
+
inputSchema: { type: "object", properties: {}, required: [] },
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: ToolName.SAVE_SESSION,
|
|
73
|
+
description: "Saves the current ASP program session to a file for later loading",
|
|
74
|
+
inputSchema: zodToJsonSchema(SaveSessionSchema),
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
name: ToolName.LOAD_SESSION,
|
|
78
|
+
description: "Loads a previously saved ASP program session",
|
|
79
|
+
inputSchema: zodToJsonSchema(LoadSessionSchema),
|
|
80
|
+
},
|
|
81
|
+
];
|
|
82
|
+
return { tools };
|
|
83
|
+
});
|
|
84
|
+
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
85
|
+
const { name, arguments: args } = request.params;
|
|
86
|
+
if (name === ToolName.SOLVE) {
|
|
87
|
+
const validatedArgs = SolveSchema.parse(args);
|
|
88
|
+
const { program, models } = validatedArgs;
|
|
89
|
+
try {
|
|
90
|
+
// Combine current session program with the provided program
|
|
91
|
+
const fullProgram = currentProgram ? `${currentProgram}\n${program}` : program;
|
|
92
|
+
// Run clingo with the combined program
|
|
93
|
+
const result = await clingo.run(fullProgram, models);
|
|
94
|
+
return {
|
|
95
|
+
content: [{
|
|
96
|
+
type: "text",
|
|
97
|
+
text: JSON.stringify(result, null, 2),
|
|
98
|
+
}]
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
catch (e) {
|
|
102
|
+
return {
|
|
103
|
+
content: [{
|
|
104
|
+
type: "text",
|
|
105
|
+
text: `Error solving program: ${e}`,
|
|
106
|
+
annotations: { priority: 1.0 }
|
|
107
|
+
}]
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
if (name === ToolName.ADD_TO_PROGRAM) {
|
|
112
|
+
const validatedArgs = AddToProgramSchema.parse(args);
|
|
113
|
+
const { code } = validatedArgs;
|
|
114
|
+
try {
|
|
115
|
+
// Append to current program with a newline separator
|
|
116
|
+
if (currentProgram) {
|
|
117
|
+
currentProgram += "\n" + code;
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
currentProgram = code;
|
|
121
|
+
}
|
|
122
|
+
return {
|
|
123
|
+
content: [{
|
|
124
|
+
type: "text",
|
|
125
|
+
text: `Added to program. Current program:\n${currentProgram}`
|
|
126
|
+
}]
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
catch (e) {
|
|
130
|
+
return {
|
|
131
|
+
content: [{
|
|
132
|
+
type: "text",
|
|
133
|
+
text: `Error adding to program: ${e}`,
|
|
134
|
+
annotations: { priority: 1.0 }
|
|
135
|
+
}]
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (name === ToolName.CLEAR_PROGRAM) {
|
|
140
|
+
currentProgram = "";
|
|
141
|
+
return {
|
|
142
|
+
content: [{
|
|
143
|
+
type: "text",
|
|
144
|
+
text: "Program cleared successfully"
|
|
145
|
+
}]
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
if (name === ToolName.GET_PROGRAM) {
|
|
149
|
+
return {
|
|
150
|
+
content: [{
|
|
151
|
+
type: "text",
|
|
152
|
+
text: currentProgram ? `Current program:\n${currentProgram}` : "No program loaded"
|
|
153
|
+
}]
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
if (name === ToolName.SAVE_SESSION) {
|
|
157
|
+
const validatedArgs = SaveSessionSchema.parse(args);
|
|
158
|
+
const filename = validatedArgs.filename;
|
|
159
|
+
try {
|
|
160
|
+
if (!currentProgram.trim()) {
|
|
161
|
+
throw new Error("No program content to save");
|
|
162
|
+
}
|
|
163
|
+
const sessionPath = path.join(SESSIONS_DIR, `${filename}.lp`);
|
|
164
|
+
fs.writeFileSync(sessionPath, currentProgram);
|
|
165
|
+
return {
|
|
166
|
+
content: [{
|
|
167
|
+
type: "text",
|
|
168
|
+
text: `Session saved to ${sessionPath}`
|
|
169
|
+
}]
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
catch (e) {
|
|
173
|
+
return {
|
|
174
|
+
content: [{
|
|
175
|
+
type: "text",
|
|
176
|
+
text: `Error saving session: ${e}`,
|
|
177
|
+
annotations: { priority: 1.0 }
|
|
178
|
+
}]
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (name === ToolName.LOAD_SESSION) {
|
|
183
|
+
const validatedArgs = LoadSessionSchema.parse(args);
|
|
184
|
+
const filename = validatedArgs.filename;
|
|
185
|
+
try {
|
|
186
|
+
const sessionPath = path.join(SESSIONS_DIR, `${filename}.lp`);
|
|
187
|
+
if (!fs.existsSync(sessionPath)) {
|
|
188
|
+
throw new Error(`Session file ${sessionPath} not found`);
|
|
189
|
+
}
|
|
190
|
+
currentProgram = fs.readFileSync(sessionPath, 'utf8');
|
|
191
|
+
return {
|
|
192
|
+
content: [{
|
|
193
|
+
type: "text",
|
|
194
|
+
text: `Session loaded from ${sessionPath}\nCurrent program:\n${currentProgram}`
|
|
195
|
+
}]
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
catch (e) {
|
|
199
|
+
return {
|
|
200
|
+
content: [{
|
|
201
|
+
type: "text",
|
|
202
|
+
text: `Error loading session: ${e}`,
|
|
203
|
+
annotations: { priority: 1.0 }
|
|
204
|
+
}]
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
209
|
+
});
|
|
210
|
+
const cleanup = async () => {
|
|
211
|
+
// Clean up any resources if needed when server shuts down
|
|
212
|
+
};
|
|
213
|
+
return { server, cleanup };
|
|
214
|
+
};
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import { createServer } from "./clingoServer.js";
|
|
4
|
+
async function main() {
|
|
5
|
+
const transport = new StdioServerTransport();
|
|
6
|
+
const { server, cleanup } = await createServer();
|
|
7
|
+
await server.connect(transport);
|
|
8
|
+
// Cleanup on exit
|
|
9
|
+
process.on("SIGINT", async () => {
|
|
10
|
+
await cleanup();
|
|
11
|
+
await server.close();
|
|
12
|
+
process.exit(0);
|
|
13
|
+
});
|
|
14
|
+
}
|
|
15
|
+
main().catch((error) => {
|
|
16
|
+
console.error("Server error:", error);
|
|
17
|
+
process.exit(1);
|
|
18
|
+
});
|
package/package.json
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "clingo-mcp",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "MCP server for interacting with Clingo ASP solver via WebAssembly",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "NewJerseyStyle",
|
|
7
|
+
"homepage": "https://github.com/NewJerseyStyle/clingo-mcp",
|
|
8
|
+
"bugs": "https://github.com/NewJerseyStyle/clingo-mcp/issues",
|
|
9
|
+
"type": "module",
|
|
10
|
+
"bin": {
|
|
11
|
+
"mcp-server-clingo": "dist/index.js"
|
|
12
|
+
},
|
|
13
|
+
"files": [
|
|
14
|
+
"dist"
|
|
15
|
+
],
|
|
16
|
+
"scripts": {
|
|
17
|
+
"build": "tsc -p tsconfig.json && shx chmod +x dist/*.js",
|
|
18
|
+
"prepare": "npm run build",
|
|
19
|
+
"watch": "tsc --watch",
|
|
20
|
+
"start": "node dist/index.js"
|
|
21
|
+
},
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@modelcontextprotocol/sdk": "1.0.1",
|
|
24
|
+
"clingo-wasm": "latest",
|
|
25
|
+
"zod": "^3.23.8",
|
|
26
|
+
"zod-to-json-schema": "^3.23.5"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^22.14.1",
|
|
30
|
+
"shx": "^0.3.4",
|
|
31
|
+
"typescript": "^5.6.2",
|
|
32
|
+
"vite": "^6.3.0"
|
|
33
|
+
}
|
|
34
|
+
}
|