@williamp29/project-mcp-server 3.0.1 → 3.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 +28 -7
- package/dist/api-explorer/tool-generator.js +5 -1
- package/dist/mcp-server.d.ts +18 -10
- package/dist/mcp-server.js +102 -60
- package/package.json +15 -1
package/README.md
CHANGED
|
@@ -65,23 +65,44 @@ const mysqlDriver = new MySQLDriver({
|
|
|
65
65
|
|
|
66
66
|
// 3. Create and start the server
|
|
67
67
|
const server = new MCPServer({
|
|
68
|
-
specPath: "./openapi-spec.json",
|
|
68
|
+
specPath: "./openapi-spec.json", // Optional: Omit to skip API explorer
|
|
69
69
|
api: {
|
|
70
70
|
baseUrl: "https://api.example.com",
|
|
71
71
|
authContext: new GlobalAuthContext(new MyCustomAuth()),
|
|
72
72
|
},
|
|
73
|
-
database: {
|
|
73
|
+
database: { // Optional: Pass undefined to skip database initialization
|
|
74
74
|
driver: mysqlDriver,
|
|
75
75
|
permissions: {
|
|
76
|
-
enableRunQuery: true,
|
|
77
|
-
enableRunUpdateStatement: true,
|
|
78
|
-
enableRunDeleteStatement: false,
|
|
79
|
-
enableRunStatement: false
|
|
76
|
+
enableRunQuery: true,
|
|
77
|
+
enableRunUpdateStatement: true,
|
|
78
|
+
enableRunDeleteStatement: false,
|
|
79
|
+
enableRunStatement: false
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
});
|
|
83
83
|
|
|
84
|
-
server.start()
|
|
84
|
+
await server.start();
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Lifecycle Management
|
|
88
|
+
|
|
89
|
+
The `MCPServer` class provides methods to manage the server's lifecycle programmatically:
|
|
90
|
+
|
|
91
|
+
| Method | Description |
|
|
92
|
+
| :--- | :--- |
|
|
93
|
+
| `start()` | Initializes the API explorer and database connection, then connects to the transport. |
|
|
94
|
+
| `stop()` | Gracefully disconnects from the database and closes the MCP server. |
|
|
95
|
+
| `reload()` | Restarts the server, clearing registered tools and re-initializing all configurations. |
|
|
96
|
+
|
|
97
|
+
Example:
|
|
98
|
+
```typescript
|
|
99
|
+
await server.start();
|
|
100
|
+
|
|
101
|
+
// Later, to restart the server with refreshed configuration:
|
|
102
|
+
await server.reload();
|
|
103
|
+
|
|
104
|
+
// To shut down the server:
|
|
105
|
+
await server.stop();
|
|
85
106
|
```
|
|
86
107
|
|
|
87
108
|
### 3. Custom Database Drivers
|
|
@@ -71,7 +71,11 @@ export class ToolGenerator {
|
|
|
71
71
|
case "api_get_tags_endpoints":
|
|
72
72
|
return this.parser.getEndpointsByTags(args.tags);
|
|
73
73
|
case "api_get_all_endpoints":
|
|
74
|
-
return this.parser.getEndpoints()
|
|
74
|
+
return this.parser.getEndpoints().map(e => ({
|
|
75
|
+
method: e.method,
|
|
76
|
+
path: e.path,
|
|
77
|
+
summary: e.summary,
|
|
78
|
+
}));
|
|
75
79
|
case "api_get_endpoint":
|
|
76
80
|
return this.parser.getEndpoint(args.method, args.path);
|
|
77
81
|
case "api_get_endpoints":
|
package/dist/mcp-server.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ export type { ApiConfig };
|
|
|
11
11
|
* 3. Impersonate users via the set_identity tool.
|
|
12
12
|
*/
|
|
13
13
|
export interface MCPServerOptions {
|
|
14
|
-
specPath
|
|
14
|
+
specPath?: string;
|
|
15
15
|
api?: ApiConfig;
|
|
16
16
|
database?: DatabaseConfig;
|
|
17
17
|
}
|
|
@@ -26,22 +26,30 @@ export interface MCPServerOptions {
|
|
|
26
26
|
*/
|
|
27
27
|
export declare class MCPServer {
|
|
28
28
|
private server;
|
|
29
|
-
private parser
|
|
30
|
-
private toolGenerator
|
|
31
|
-
private apiExecutor
|
|
32
|
-
private authContext
|
|
29
|
+
private parser?;
|
|
30
|
+
private toolGenerator?;
|
|
31
|
+
private apiExecutor?;
|
|
32
|
+
private authContext?;
|
|
33
33
|
private dbExecutor?;
|
|
34
34
|
private dbToolGenerator?;
|
|
35
|
-
private specPath
|
|
35
|
+
private specPath?;
|
|
36
|
+
private transport?;
|
|
37
|
+
private options;
|
|
36
38
|
/**
|
|
37
39
|
* @param options - Configuration options for the MCP Server.
|
|
38
40
|
*/
|
|
39
41
|
constructor(options: MCPServerOptions);
|
|
40
|
-
private
|
|
42
|
+
private initApiExplorer;
|
|
43
|
+
private initDatabase;
|
|
41
44
|
private registerTools;
|
|
45
|
+
start(): Promise<void>;
|
|
42
46
|
/**
|
|
43
|
-
*
|
|
44
|
-
* This method ensures the OpenAPI spec is loaded and tools are registered before connecting.
|
|
47
|
+
* Stops the MCP server and disconnects any database connections.
|
|
45
48
|
*/
|
|
46
|
-
|
|
49
|
+
stop(): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Reloads the MCP server by stopping it and starting it again.
|
|
52
|
+
* This will re-initialize the OpenAPI parser and database connection.
|
|
53
|
+
*/
|
|
54
|
+
reload(): Promise<void>;
|
|
47
55
|
}
|
package/dist/mcp-server.js
CHANGED
|
@@ -24,17 +24,22 @@ export class MCPServer {
|
|
|
24
24
|
dbExecutor;
|
|
25
25
|
dbToolGenerator;
|
|
26
26
|
specPath;
|
|
27
|
+
transport;
|
|
28
|
+
options;
|
|
27
29
|
/**
|
|
28
30
|
* @param options - Configuration options for the MCP Server.
|
|
29
31
|
*/
|
|
30
32
|
constructor(options) {
|
|
33
|
+
this.options = options;
|
|
31
34
|
this.specPath = options.specPath;
|
|
32
|
-
this.
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
if (this.specPath) {
|
|
36
|
+
this.parser = new OpenAPIParser();
|
|
37
|
+
this.toolGenerator = new ToolGenerator(this.parser);
|
|
38
|
+
this.authContext = options.api?.authContext || createAuthContextFromEnv();
|
|
39
|
+
const apiBaseUrl = options.api?.baseUrl;
|
|
40
|
+
this.apiExecutor = new ApiExecutor(apiBaseUrl, this.authContext);
|
|
41
|
+
}
|
|
42
|
+
const dbConfig = 'database' in options ? options.database : createDbConfigFromEnv();
|
|
38
43
|
if (dbConfig) {
|
|
39
44
|
this.dbExecutor = new DbExecutor(dbConfig.driver, dbConfig.permissions);
|
|
40
45
|
this.dbToolGenerator = new DbToolGenerator(dbConfig.driver, dbConfig.permissions);
|
|
@@ -44,47 +49,58 @@ export class MCPServer {
|
|
|
44
49
|
version: "1.0.0",
|
|
45
50
|
});
|
|
46
51
|
}
|
|
47
|
-
async
|
|
52
|
+
async initApiExplorer() {
|
|
53
|
+
if (!this.parser || !this.specPath)
|
|
54
|
+
return;
|
|
48
55
|
try {
|
|
49
56
|
await this.parser.loadSpec(this.specPath);
|
|
50
57
|
console.error(`Loaded OpenAPI spec from ${this.specPath}`);
|
|
51
|
-
if (this.dbExecutor) {
|
|
52
|
-
await this.dbExecutor.connect();
|
|
53
|
-
}
|
|
54
|
-
this.registerTools();
|
|
55
58
|
}
|
|
56
59
|
catch (error) {
|
|
57
60
|
console.error(`Failed to load OpenAPI spec: ${error}`);
|
|
58
61
|
process.exit(1);
|
|
59
62
|
}
|
|
60
63
|
}
|
|
64
|
+
async initDatabase() {
|
|
65
|
+
if (this.dbExecutor) {
|
|
66
|
+
try {
|
|
67
|
+
await this.dbExecutor.connect();
|
|
68
|
+
console.error("Connected to database");
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
console.error(`Failed to connect to database: ${error}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
61
75
|
registerTools() {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
76
|
+
if (this.toolGenerator && this.apiExecutor) {
|
|
77
|
+
const definitions = this.toolGenerator.getToolDefinitions();
|
|
78
|
+
Object.entries(definitions).forEach(([name, def]) => {
|
|
79
|
+
this.server.registerTool(name, {
|
|
80
|
+
description: def.description,
|
|
81
|
+
inputSchema: def.inputSchema,
|
|
82
|
+
}, async (args) => {
|
|
83
|
+
try {
|
|
84
|
+
let result;
|
|
85
|
+
if (name === "api_call_endpoint") {
|
|
86
|
+
result = await this.apiExecutor.callEndpoint(args);
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
result = this.toolGenerator.handleToolCall(name, args);
|
|
90
|
+
}
|
|
91
|
+
return {
|
|
92
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
93
|
+
};
|
|
72
94
|
}
|
|
73
|
-
|
|
74
|
-
|
|
95
|
+
catch (error) {
|
|
96
|
+
return {
|
|
97
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
98
|
+
isError: true,
|
|
99
|
+
};
|
|
75
100
|
}
|
|
76
|
-
|
|
77
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
78
|
-
};
|
|
79
|
-
}
|
|
80
|
-
catch (error) {
|
|
81
|
-
return {
|
|
82
|
-
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
83
|
-
isError: true,
|
|
84
|
-
};
|
|
85
|
-
}
|
|
101
|
+
});
|
|
86
102
|
});
|
|
87
|
-
}
|
|
103
|
+
}
|
|
88
104
|
if (this.dbToolGenerator) {
|
|
89
105
|
const dbDefinitions = this.dbToolGenerator.getToolDefinitions();
|
|
90
106
|
Object.entries(dbDefinitions).forEach(([name, def]) => {
|
|
@@ -108,34 +124,60 @@ export class MCPServer {
|
|
|
108
124
|
});
|
|
109
125
|
});
|
|
110
126
|
}
|
|
111
|
-
this.
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
127
|
+
if (this.authContext) {
|
|
128
|
+
this.server.registerTool("api_set_identity", {
|
|
129
|
+
description: "Set the identity (identifier) for future API requests. Requires an 'identity' auth strategy to be active.",
|
|
130
|
+
inputSchema: z.object({
|
|
131
|
+
identifier: z.string().describe("The new identity value (e.g., user UID)."),
|
|
132
|
+
}),
|
|
133
|
+
}, async ({ identifier }) => {
|
|
134
|
+
try {
|
|
135
|
+
this.authContext.setIdentifier(identifier);
|
|
136
|
+
return {
|
|
137
|
+
content: [{ type: "text", text: `Successfully updated identity to: ${identifier}` }],
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
return {
|
|
142
|
+
content: [{ type: "text", text: `Error: ${error.message}` }],
|
|
143
|
+
isError: true,
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
}
|
|
130
148
|
}
|
|
131
|
-
/**
|
|
132
|
-
* Starts the MCP server on Stdio transport.
|
|
133
|
-
* This method ensures the OpenAPI spec is loaded and tools are registered before connecting.
|
|
134
|
-
*/
|
|
135
149
|
async start() {
|
|
136
|
-
await this.
|
|
137
|
-
|
|
138
|
-
|
|
150
|
+
await this.initApiExplorer();
|
|
151
|
+
await this.initDatabase();
|
|
152
|
+
this.registerTools();
|
|
153
|
+
if (!this.transport) {
|
|
154
|
+
this.transport = new StdioServerTransport();
|
|
155
|
+
}
|
|
156
|
+
await this.server.connect(this.transport);
|
|
139
157
|
console.error("OpenAPI MCP server running on stdio");
|
|
140
158
|
}
|
|
159
|
+
/**
|
|
160
|
+
* Stops the MCP server and disconnects any database connections.
|
|
161
|
+
*/
|
|
162
|
+
async stop() {
|
|
163
|
+
if (this.dbExecutor) {
|
|
164
|
+
await this.dbExecutor.disconnect();
|
|
165
|
+
}
|
|
166
|
+
await this.server.close();
|
|
167
|
+
this.transport = undefined;
|
|
168
|
+
console.error("MCP server stopped");
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Reloads the MCP server by stopping it and starting it again.
|
|
172
|
+
* This will re-initialize the OpenAPI parser and database connection.
|
|
173
|
+
*/
|
|
174
|
+
async reload() {
|
|
175
|
+
await this.stop();
|
|
176
|
+
this.server = new McpServer({
|
|
177
|
+
name: "project-mcp-server",
|
|
178
|
+
version: "1.0.0",
|
|
179
|
+
});
|
|
180
|
+
await this.start();
|
|
181
|
+
console.error("MCP server reloaded");
|
|
182
|
+
}
|
|
141
183
|
}
|
package/package.json
CHANGED
|
@@ -1,10 +1,24 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@williamp29/project-mcp-server",
|
|
3
|
-
"version": "3.0
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "A ModelContextProtocol server to let agents discover your project, such as APIs (using OpenAPI) or other resources.",
|
|
5
5
|
"repository": "https://github.com/WilliamPinto-Olmos/project-mcp-server",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
|
+
"license": "MIT",
|
|
9
|
+
"keywords": [
|
|
10
|
+
"mcp",
|
|
11
|
+
"model-context-protocol",
|
|
12
|
+
"ai",
|
|
13
|
+
"agent",
|
|
14
|
+
"openapi",
|
|
15
|
+
"database",
|
|
16
|
+
"api",
|
|
17
|
+
"sql",
|
|
18
|
+
"cursor",
|
|
19
|
+
"claudecode",
|
|
20
|
+
"copilot"
|
|
21
|
+
],
|
|
8
22
|
"bin": {
|
|
9
23
|
"project-mcp-server": "dist/cli.js"
|
|
10
24
|
},
|