@access-mcp/shared 0.3.0 → 0.3.1

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.
@@ -8,12 +8,32 @@ export declare abstract class BaseAccessServer {
8
8
  protected server: Server;
9
9
  protected transport: StdioServerTransport;
10
10
  private _httpClient?;
11
+ private _httpServer?;
12
+ private _httpPort?;
11
13
  constructor(serverName: string, version: string, baseURL?: string);
12
14
  protected get httpClient(): AxiosInstance;
13
15
  private setupHandlers;
14
16
  protected abstract getTools(): any[];
15
17
  protected abstract getResources(): any[];
16
18
  protected abstract handleToolCall(request: any): Promise<any>;
17
- protected abstract handleResourceRead(request: any): Promise<any>;
18
- start(): Promise<void>;
19
+ protected handleResourceRead(request: any): Promise<any>;
20
+ /**
21
+ * Start the MCP server with optional HTTP service layer for inter-server communication
22
+ */
23
+ start(options?: {
24
+ httpPort?: number;
25
+ }): Promise<void>;
26
+ /**
27
+ * Start HTTP service layer for inter-server communication
28
+ */
29
+ private startHttpService;
30
+ /**
31
+ * Call a tool on another ACCESS-CI MCP server via HTTP
32
+ */
33
+ protected callRemoteServer(serviceName: string, toolName: string, args?: Record<string, any>): Promise<any>;
34
+ /**
35
+ * Get service endpoint from environment configuration
36
+ * Expected format: ACCESS_MCP_SERVICES=nsf-awards=http://localhost:3001,xdmod-metrics=http://localhost:3002
37
+ */
38
+ private getServiceEndpoint;
19
39
  }
@@ -2,6 +2,7 @@ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
2
2
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
3
3
  import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema, } from "@modelcontextprotocol/sdk/types.js";
4
4
  import axios from "axios";
5
+ import express from "express";
5
6
  export class BaseAccessServer {
6
7
  serverName;
7
8
  version;
@@ -9,6 +10,8 @@ export class BaseAccessServer {
9
10
  server;
10
11
  transport;
11
12
  _httpClient;
13
+ _httpServer;
14
+ _httpPort;
12
15
  constructor(serverName, version, baseURL = "https://support.access-ci.org/api") {
13
16
  this.serverName = serverName;
14
17
  this.version = version;
@@ -99,9 +102,120 @@ export class BaseAccessServer {
99
102
  }
100
103
  });
101
104
  }
102
- async start() {
103
- await this.server.connect(this.transport);
104
- // MCP servers should not output anything to stderr/stdout when running
105
- // as it interferes with JSON-RPC communication
105
+ async handleResourceRead(request) {
106
+ throw new Error("Resource reading not supported by this server");
107
+ }
108
+ /**
109
+ * Start the MCP server with optional HTTP service layer for inter-server communication
110
+ */
111
+ async start(options) {
112
+ // Start HTTP service layer if port is specified
113
+ if (options?.httpPort) {
114
+ this._httpPort = options.httpPort;
115
+ await this.startHttpService();
116
+ console.log(`${this.serverName} HTTP server running on port ${this._httpPort}`);
117
+ }
118
+ else {
119
+ // Only connect stdio transport when NOT in HTTP mode
120
+ await this.server.connect(this.transport);
121
+ // MCP servers should not output anything to stderr/stdout when running
122
+ // as it interferes with JSON-RPC communication
123
+ }
124
+ }
125
+ /**
126
+ * Start HTTP service layer for inter-server communication
127
+ */
128
+ async startHttpService() {
129
+ if (!this._httpPort)
130
+ return;
131
+ this._httpServer = express();
132
+ this._httpServer.use(express.json());
133
+ // Health check endpoint
134
+ this._httpServer.get('/health', (req, res) => {
135
+ res.json({
136
+ server: this.serverName,
137
+ version: this.version,
138
+ status: 'healthy',
139
+ timestamp: new Date().toISOString()
140
+ });
141
+ });
142
+ // List available tools endpoint
143
+ this._httpServer.get('/tools', (req, res) => {
144
+ try {
145
+ const tools = this.getTools();
146
+ res.json({ tools });
147
+ }
148
+ catch (error) {
149
+ res.status(500).json({ error: 'Failed to list tools' });
150
+ }
151
+ });
152
+ // Tool execution endpoint
153
+ this._httpServer.post('/tools/:toolName', async (req, res) => {
154
+ try {
155
+ const { toolName } = req.params;
156
+ const { arguments: args = {} } = req.body;
157
+ // Validate that the tool exists
158
+ const tools = this.getTools();
159
+ const tool = tools.find(t => t.name === toolName);
160
+ if (!tool) {
161
+ return res.status(404).json({ error: `Tool '${toolName}' not found` });
162
+ }
163
+ // Execute the tool
164
+ const request = {
165
+ params: {
166
+ name: toolName,
167
+ arguments: args
168
+ }
169
+ };
170
+ const result = await this.handleToolCall(request);
171
+ res.json(result);
172
+ }
173
+ catch (error) {
174
+ const errorMessage = error instanceof Error ? error.message : String(error);
175
+ res.status(500).json({ error: errorMessage });
176
+ }
177
+ });
178
+ // Start HTTP server
179
+ return new Promise((resolve, reject) => {
180
+ this._httpServer.listen(this._httpPort, '0.0.0.0', () => {
181
+ resolve();
182
+ }).on('error', reject);
183
+ });
184
+ }
185
+ /**
186
+ * Call a tool on another ACCESS-CI MCP server via HTTP
187
+ */
188
+ async callRemoteServer(serviceName, toolName, args = {}) {
189
+ const serviceUrl = this.getServiceEndpoint(serviceName);
190
+ if (!serviceUrl) {
191
+ throw new Error(`Service '${serviceName}' not found. Check ACCESS_MCP_SERVICES environment variable.`);
192
+ }
193
+ const response = await axios.post(`${serviceUrl}/tools/${toolName}`, {
194
+ arguments: args
195
+ }, {
196
+ timeout: 30000,
197
+ validateStatus: () => true
198
+ });
199
+ if (response.status !== 200) {
200
+ throw new Error(`Remote server call failed: ${response.status} ${response.data?.error || response.statusText}`);
201
+ }
202
+ return response.data;
203
+ }
204
+ /**
205
+ * Get service endpoint from environment configuration
206
+ * Expected format: ACCESS_MCP_SERVICES=nsf-awards=http://localhost:3001,xdmod-metrics=http://localhost:3002
207
+ */
208
+ getServiceEndpoint(serviceName) {
209
+ const services = process.env.ACCESS_MCP_SERVICES;
210
+ if (!services)
211
+ return null;
212
+ const serviceMap = {};
213
+ services.split(',').forEach(service => {
214
+ const [name, url] = service.split('=');
215
+ if (name && url) {
216
+ serviceMap[name.trim()] = url.trim();
217
+ }
218
+ });
219
+ return serviceMap[serviceName] || null;
106
220
  }
107
221
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@access-mcp/shared",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Shared utilities for ACCESS-CI MCP servers",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -34,6 +34,10 @@
34
34
  "dependencies": {
35
35
  "@modelcontextprotocol/sdk": "^1.16.0",
36
36
  "axios": "^1.6.0",
37
+ "express": "^5.1.0",
37
38
  "zod": "^3.22.0"
39
+ },
40
+ "devDependencies": {
41
+ "@types/express": "^5.0.0"
38
42
  }
39
43
  }