Skip to content

Commit cff6885

Browse files
committed
to npm
1 parent b79b003 commit cff6885

File tree

5 files changed

+80
-76
lines changed

5 files changed

+80
-76
lines changed

README.md

Lines changed: 64 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,56 @@ This server uses the **official `@modelcontextprotocol/sdk`** package from Anthr
5353
- npm 10+
5454
- FAIM API key (set as `FAIM_API_KEY` environment variable)
5555

56-
### Setup
56+
### Option 1: Install from npm (Recommended)
5757

5858
```bash
59+
npm install @faim-group/mcp
60+
```
61+
62+
Then configure Claude Desktop to use it:
63+
64+
```json
65+
{
66+
"mcpServers": {
67+
"faim": {
68+
"command": "node",
69+
"args": ["-e", "require('@faim-group/mcp')"],
70+
"env": {
71+
"FAIM_API_KEY": "your-api-key-here"
72+
}
73+
}
74+
}
75+
}
76+
```
77+
78+
Or if installed globally:
79+
80+
```bash
81+
npm install -g @faim-group/mcp
82+
```
83+
84+
Then in Claude Desktop config:
85+
86+
```json
87+
{
88+
"mcpServers": {
89+
"faim": {
90+
"command": "faim-mcp",
91+
"env": {
92+
"FAIM_API_KEY": "your-api-key-here"
93+
}
94+
}
95+
}
96+
}
97+
```
98+
99+
### Option 2: Clone and Build Locally
100+
101+
```bash
102+
# Clone the repository
103+
git clone <repository-url>
104+
cd faim-mcp
105+
59106
# Install dependencies
60107
npm install
61108

@@ -69,6 +116,22 @@ npm test
69116
npm run lint
70117
```
71118

119+
Then in Claude Desktop config, use the local path:
120+
121+
```json
122+
{
123+
"mcpServers": {
124+
"faim": {
125+
"command": "node",
126+
"args": ["/path/to/faim-mcp/dist/index.js"],
127+
"env": {
128+
"FAIM_API_KEY": "your-api-key-here"
129+
}
130+
}
131+
}
132+
}
133+
```
134+
72135
## Configuration
73136

74137
### Environment Variables

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
2-
"name": "faim-mcp",
3-
"version": "1.0.0",
2+
"name": "@faim-group/mcp",
3+
"version": "0.1.0",
44
"description": "MCP server for FAIM forecasting SDK - Enables Claude to perform time series forecasting using Chronos2 and TiRex models",
55
"main": "dist/index.js",
66
"types": "dist/index.d.ts",

src/index.ts

Lines changed: 14 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
* and ensuring compatibility with the MCP specification.
5959
*/
6060

61+
import { z } from 'zod';
6162
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
6263
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
6364
import { initializeClient } from './utils/client.js';
@@ -90,7 +91,7 @@ server.registerTool(
9091
{
9192
description:
9293
'List all available forecasting models and their capabilities. Returns information about Chronos2, TiRex, and other available models, including supported output types and features.',
93-
inputSchema: {},
94+
inputSchema: z.object({}).strict() as any,
9495
},
9596
async () => {
9697
const result = await listModels();
@@ -122,72 +123,23 @@ server.registerTool(
122123
{
123124
description:
124125
'Perform time series forecasting using FAIM models. Supports both point forecasting (single value) and probabilistic forecasting (confidence intervals). Can handle univariate and multivariate time series data.',
125-
inputSchema: {
126-
type: 'object' as const,
127-
properties: {
128-
model: {
129-
type: 'string' as const,
130-
enum: ['chronos2', 'tirex'],
131-
description:
132-
'The forecasting model to use. Chronos2 is the general-purpose model. TiRex is an alternative with different characteristics.',
133-
},
134-
x: {
135-
description:
136-
'Time series data to forecast from. Can be a 1D array (single series), 2D array (multiple series or multivariate), or 3D array. 1D example: [1,2,3,4,5]. 2D example: [[1,2],[3,4],[5,6]].',
137-
oneOf: [
138-
{
139-
type: 'array' as const,
140-
items: { type: 'number' as const },
141-
description: '1D array: single univariate time series',
142-
},
143-
{
144-
type: 'array' as const,
145-
items: {
146-
type: 'array' as const,
147-
items: { type: 'number' as const },
148-
},
149-
description: '2D array: multiple timesteps with features',
150-
},
151-
{
152-
type: 'array' as const,
153-
items: {
154-
type: 'array' as const,
155-
items: {
156-
type: 'array' as const,
157-
items: { type: 'number' as const },
158-
},
159-
},
160-
description: '3D array: batch of time series',
161-
},
162-
],
163-
},
164-
horizon: {
165-
type: 'number' as const,
166-
description:
167-
'Number of time steps to forecast into the future. Must be a positive integer. Example: 10 means predict the next 10 steps.',
168-
},
169-
output_type: {
170-
type: 'string' as const,
171-
enum: ['point', 'quantiles'],
172-
default: 'point',
173-
description:
174-
'Type of forecast output. "point" = single value per step (fastest). "quantiles" = confidence intervals (use for uncertainty).',
175-
},
176-
quantiles: {
177-
type: 'array' as const,
178-
items: { type: 'number' as const },
179-
description:
180-
'Quantile levels to compute (only used with output_type="quantiles"). Values between 0 and 1. Example: [0.1, 0.5, 0.9] for 10th, 50th, 90th percentiles.',
181-
},
182-
},
183-
required: ['model', 'x', 'horizon'] as const,
184-
} as any,
126+
inputSchema: z.object({
127+
model: z.enum(['chronos2', 'tirex']).describe('The forecasting model to use. Chronos2 is the general-purpose model. TiRex is an alternative with different characteristics.'),
128+
x: z.union([
129+
z.array(z.number()).describe('1D array: single univariate time series'),
130+
z.array(z.array(z.number())).describe('2D array: multiple timesteps with features'),
131+
z.array(z.array(z.array(z.number()))).describe('3D array: batch of time series'),
132+
]).describe('Time series data to forecast from. Can be a 1D array (single series), 2D array (multiple series or multivariate), or 3D array.'),
133+
horizon: z.number().describe('Number of time steps to forecast into the future. Must be a positive integer. Example: 10 means predict the next 10 steps.'),
134+
output_type: z.enum(['point', 'quantiles']).default('point').describe('Type of forecast output. "point" = single value per step (fastest). "quantiles" = confidence intervals (use for uncertainty).'),
135+
quantiles: z.array(z.number()).optional().describe('Quantile levels to compute (only used with output_type="quantiles"). Values between 0 and 1. Example: [0.1, 0.5, 0.9] for 10th, 50th, 90th percentiles.'),
136+
}).strict() as any,
185137
},
186138
async (args: unknown) => {
187139
const result = await forecast(args);
188140

189141
if (!result.success) {
190-
throw new Error(JSON.stringify(result.error));
142+
throw new Error(result.error.message);
191143
}
192144

193145
// Format response for MCP
@@ -217,7 +169,6 @@ async function main(): Promise<void> {
217169
try {
218170
// Initialize FAIM client before accepting requests
219171
initializeClient();
220-
console.error('[MCP] FAIM client initialized successfully');
221172

222173
// Create transport for stdio communication
223174
const transport = new StdioServerTransport();
@@ -226,8 +177,6 @@ async function main(): Promise<void> {
226177
// The transport handles all JSON-RPC protocol details
227178
await server.connect(transport);
228179

229-
console.error('[MCP] Server started with official @modelcontextprotocol/sdk');
230-
console.error('[MCP] Waiting for requests from Claude...');
231180
} catch (error) {
232181
console.error('[MCP] Failed to start server:', error);
233182
process.exit(1);

src/tools/forecast.ts

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,8 +45,6 @@ export async function forecast(
4545
const normalizedX = normalizeInput(forecastRequest.x);
4646
const outputShape = getArrayShape(normalizedX);
4747

48-
// Log the operation for debugging
49-
console.log(`Forecast request: model=${forecastRequest.model}, horizon=${forecastRequest.horizon}, output_type=${forecastRequest.output_type || 'point'}`);
5048

5149
// Get the FAIM client (singleton initialized at server startup)
5250
const client = getClient();
@@ -122,17 +120,13 @@ export async function forecast(
122120
},
123121
};
124122

125-
// Log successful forecast
126-
console.log(`Forecast completed successfully: ${duration}ms, tokens=${sdkResponse.metadata.token_count}`);
127123

128124
return {
129125
success: true,
130126
data: response,
131127
};
132128
} catch (error) {
133129
// Catch all errors and transform to user-friendly format
134-
console.error('Forecast tool error:', error);
135-
136130
return {
137131
success: false,
138132
error: transformError(error, { operation: 'forecast' }),

src/utils/client.ts

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,6 @@ export function initializeClient(): void {
8787
baseUrl: process.env.FAIM_API_BASE_URL,
8888
});
8989

90-
// Log successful initialization (without exposing the API key)
91-
console.log('FAIM client initialized successfully');
9290
} catch (error) {
9391
// Store the error for later retrieval
9492
initializationError = error instanceof Error ? error : new Error(String(error));

0 commit comments

Comments
 (0)