@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 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, // Default: true
77
- enableRunUpdateStatement: true, // Default: true
78
- enableRunDeleteStatement: false, // Default: false
79
- enableRunStatement: false // Default: false
76
+ enableRunQuery: true,
77
+ enableRunUpdateStatement: true,
78
+ enableRunDeleteStatement: false,
79
+ enableRunStatement: false
80
80
  }
81
81
  }
82
82
  });
83
83
 
84
- server.start().catch(console.error);
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":
@@ -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: string;
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 initParser;
42
+ private initApiExplorer;
43
+ private initDatabase;
41
44
  private registerTools;
45
+ start(): Promise<void>;
42
46
  /**
43
- * Starts the MCP server on Stdio transport.
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
- start(): Promise<void>;
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
  }
@@ -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.parser = new OpenAPIParser();
33
- this.toolGenerator = new ToolGenerator(this.parser);
34
- this.authContext = options.api?.authContext || createAuthContextFromEnv();
35
- const apiBaseUrl = options.api?.baseUrl;
36
- const dbConfig = options.database || createDbConfigFromEnv();
37
- this.apiExecutor = new ApiExecutor(apiBaseUrl, this.authContext);
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 initParser() {
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
- const definitions = this.toolGenerator.getToolDefinitions();
63
- Object.entries(definitions).forEach(([name, def]) => {
64
- this.server.registerTool(name, {
65
- description: def.description,
66
- inputSchema: def.inputSchema,
67
- }, async (args) => {
68
- try {
69
- let result;
70
- if (name === "api_call_endpoint") {
71
- result = await this.apiExecutor.callEndpoint(args);
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
- else {
74
- result = this.toolGenerator.handleToolCall(name, args);
95
+ catch (error) {
96
+ return {
97
+ content: [{ type: "text", text: `Error: ${error.message}` }],
98
+ isError: true,
99
+ };
75
100
  }
76
- return {
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.server.registerTool("api_set_identity", {
112
- description: "Set the identity (identifier) for future API requests. Requires an 'identity' auth strategy to be active.",
113
- inputSchema: z.object({
114
- identifier: z.string().describe("The new identity value (e.g., user UID)."),
115
- }),
116
- }, async ({ identifier }) => {
117
- try {
118
- this.authContext.setIdentifier(identifier);
119
- return {
120
- content: [{ type: "text", text: `Successfully updated identity to: ${identifier}` }],
121
- };
122
- }
123
- catch (error) {
124
- return {
125
- content: [{ type: "text", text: `Error: ${error.message}` }],
126
- isError: true,
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.initParser();
137
- const transport = new StdioServerTransport();
138
- await this.server.connect(transport);
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.1",
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
  },