# Directory Structure
```
├── .gitignore
├── index.ts
├── package-lock.json
├── package.json
└── README.md
```
# Files
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
```
1 | node_modules
```
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
```json
1 | {
2 | "name": "mock-data-mcp",
3 | "version": "1.0.0",
4 | "description": "",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "keywords": [],
10 | "author": "",
11 | "license": "ISC",
12 | "type": "commonjs",
13 | "dependencies": {
14 | "@modelcontextprotocol/sdk": "^1.6.1",
15 | "chalk": "^5.4.1"
16 | },
17 | "devDependencies": {
18 | "@faker-js/faker": "^9.6.0",
19 | "@types/node": "^22.13.10"
20 | }
21 | }
22 |
```
--------------------------------------------------------------------------------
/index.ts:
--------------------------------------------------------------------------------
```typescript
1 | #!/usr/bin/env node
2 |
3 | import { Server } from "@modelcontextprotocol/sdk/server/index.js";
4 | import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5 | import {
6 | CallToolRequestSchema,
7 | ListToolsRequestSchema,
8 | Tool,
9 | } from "@modelcontextprotocol/sdk/types.js";
10 | import { faker, Faker, fakerEN } from '@faker-js/faker';
11 | import chalk from 'chalk';
12 |
13 | // Add type definitions
14 | type FieldDefinition = {
15 | name: string;
16 | type: string;
17 | options?: Record<string, any>;
18 | };
19 |
20 | // Create locale-specific faker instances
21 | const createLocaleFaker = (locale: string): Faker => {
22 | // For now, we'll use English as the default locale since locale support needs more setup
23 | return fakerEN;
24 | };
25 |
26 | // Define the mock data generation tools
27 | const MOCK_DATA_TOOLS: Tool[] = [
28 | {
29 | name: "generateCustomData",
30 | description: `Generates mock data based on custom field definitions.
31 |
32 | Parameters:
33 | - locale: The locale to use for generating data (e.g., "en", "es", "fr")
34 | - fields: Array of field definitions, each containing:
35 | - name: Name of the field
36 | - type: Type of data to generate
37 | - options: Optional parameters for data generation
38 |
39 | Available types:
40 | Person:
41 | - "firstName" | "lastName" | "fullName" | "gender" | "prefix" | "suffix" | "jobTitle" | "jobType" | "jobArea"
42 |
43 | Internet:
44 | - "email" | "userName" | "password" | "url" | "ipAddress" | "domainName" | "protocol" | "httpMethod"
45 |
46 | Location:
47 | - "address" | "city" | "country" | "countryCode" | "zipCode" | "state" | "stateAbbr" | "latitude" | "longitude" | "timeZone"
48 |
49 | Date/Time:
50 | - "date" (options: past, future, between) | "weekday" | "month" | "timestamp"
51 |
52 | Commerce:
53 | - "product" | "productName" | "price" (options: min, max) | "department" | "productMaterial" | "productDescription"
54 |
55 | Company:
56 | - "companyName" | "catchPhrase" | "bs" | "bsAdjective" | "bsBuzz" | "bsNoun"
57 |
58 | Finance:
59 | - "accountNumber" | "accountName" | "amount" | "currencyCode" | "currencyName" | "currencySymbol" | "bitcoinAddress"
60 |
61 | Vehicle:
62 | - "vehicle" | "manufacturer" | "model" | "type" | "fuel" | "vin" | "color"
63 |
64 | System:
65 | - "fileName" | "mimeType" | "fileExt" | "directoryPath" | "semver"
66 |
67 | Science:
68 | - "chemicalElement" | "unit" | "scientificUnit"
69 |
70 | Music:
71 | - "genre" | "songName" | "artist"
72 |
73 | Primitive Types:
74 | - "number" (options: min, max) | "float" (options: min, max, precision)
75 | - "word" | "words" | "sentence" | "paragraph" | "text"
76 | - "uuid" | "boolean"
77 | - "color" (options: format: 'hex'|'rgb')
78 | `,
79 | inputSchema: {
80 | type: "object",
81 | properties: {
82 | locale: {
83 | type: "string",
84 | description: "Locale for generating data",
85 | default: "en"
86 | },
87 | fields: {
88 | type: "array",
89 | items: {
90 | type: "object",
91 | properties: {
92 | name: {
93 | type: "string",
94 | description: "Name of the field"
95 | },
96 | type: {
97 | type: "string",
98 | description: "Type of data to generate"
99 | },
100 | options: {
101 | type: "object",
102 | description: "Optional parameters for data generation",
103 | additionalProperties: true
104 | }
105 | },
106 | required: ["name", "type"]
107 | },
108 | description: "Field definitions for data generation"
109 | }
110 | },
111 | required: ["fields"]
112 | }
113 | },
114 | {
115 | name: "generatePerson",
116 | description: `Generates mock person data including name, email, address, etc.
117 |
118 | Parameters:
119 | - locale: The locale to use for generating data (e.g., "en", "es", "fr")
120 | - fields: Array of fields to include in the generated data
121 | Available fields: firstName, lastName, email, phone, address, dateOfBirth
122 | `,
123 | inputSchema: {
124 | type: "object",
125 | properties: {
126 | locale: {
127 | type: "string",
128 | description: "Locale for generating data",
129 | default: "en"
130 | },
131 | fields: {
132 | type: "array",
133 | items: {
134 | type: "string",
135 | enum: ["firstName", "lastName", "email", "phone", "address", "dateOfBirth"]
136 | },
137 | description: "Fields to include in the generated data"
138 | }
139 | },
140 | required: ["fields"]
141 | }
142 | },
143 | {
144 | name: "generateCompany",
145 | description: `Generates mock company data including name, industry, address, etc.
146 |
147 | Parameters:
148 | - locale: The locale to use for generating data
149 | - fields: Array of fields to include in the generated data
150 | Available fields: name, industry, catchPhrase, address, phone
151 | `,
152 | inputSchema: {
153 | type: "object",
154 | properties: {
155 | locale: {
156 | type: "string",
157 | description: "Locale for generating data",
158 | default: "en"
159 | },
160 | fields: {
161 | type: "array",
162 | items: {
163 | type: "string",
164 | enum: ["name", "industry", "catchPhrase", "address", "phone"]
165 | },
166 | description: "Fields to include in the generated data"
167 | }
168 | },
169 | required: ["fields"]
170 | }
171 | }
172 | ];
173 |
174 | class MockDataServer {
175 | private generateCustomData(locale: string, fields: FieldDefinition[]) {
176 | const data: Record<string, any> = {};
177 | const localeFaker = createLocaleFaker(locale);
178 |
179 | fields.forEach(field => {
180 | const { name, type, options = {} } = field;
181 |
182 | try {
183 | switch (type) {
184 | case "firstName":
185 | data[name] = localeFaker.person.firstName();
186 | break;
187 | case "lastName":
188 | data[name] = localeFaker.person.lastName();
189 | break;
190 | case "fullName":
191 | data[name] = localeFaker.person.fullName();
192 | break;
193 | case "email":
194 | data[name] = localeFaker.internet.email();
195 | break;
196 | case "phone":
197 | data[name] = localeFaker.phone.number();
198 | break;
199 | case "number":
200 | data[name] = localeFaker.number.int({
201 | min: options.min || 0,
202 | max: options.max || 1000
203 | });
204 | break;
205 | case "date":
206 | if (options.past) {
207 | data[name] = localeFaker.date.past().toISOString();
208 | } else if (options.future) {
209 | data[name] = localeFaker.date.future().toISOString();
210 | } else {
211 | data[name] = localeFaker.date.recent().toISOString();
212 | }
213 | break;
214 | case "address":
215 | data[name] = localeFaker.location.streetAddress();
216 | break;
217 | case "city":
218 | data[name] = localeFaker.location.city();
219 | break;
220 | case "country":
221 | data[name] = localeFaker.location.country();
222 | break;
223 | case "zipCode":
224 | data[name] = localeFaker.location.zipCode();
225 | break;
226 | case "word":
227 | data[name] = localeFaker.word.sample();
228 | break;
229 | case "sentence":
230 | data[name] = localeFaker.lorem.sentence();
231 | break;
232 | case "paragraph":
233 | data[name] = localeFaker.lorem.paragraph();
234 | break;
235 | case "uuid":
236 | data[name] = localeFaker.string.uuid();
237 | break;
238 | case "boolean":
239 | data[name] = localeFaker.datatype.boolean();
240 | break;
241 | case "url":
242 | data[name] = localeFaker.internet.url();
243 | break;
244 | case "ipAddress":
245 | data[name] = localeFaker.internet.ip();
246 | break;
247 | case "color":
248 | data[name] = options.format === 'rgb'
249 | ? `rgb(${localeFaker.number.int({ min: 0, max: 255 })}, ${localeFaker.number.int({ min: 0, max: 255 })}, ${localeFaker.number.int({ min: 0, max: 255 })})`
250 | : `#${localeFaker.string.hexadecimal({ length: 6 }).substring(2)}`;
251 | break;
252 | case "gender":
253 | data[name] = localeFaker.person.gender();
254 | break;
255 | case "prefix":
256 | data[name] = localeFaker.person.prefix();
257 | break;
258 | case "suffix":
259 | data[name] = localeFaker.person.suffix();
260 | break;
261 | case "jobTitle":
262 | data[name] = localeFaker.person.jobTitle();
263 | break;
264 | case "jobType":
265 | data[name] = localeFaker.person.jobType();
266 | break;
267 | case "jobArea":
268 | data[name] = localeFaker.person.jobArea();
269 | break;
270 | case "userName":
271 | data[name] = localeFaker.internet.userName();
272 | break;
273 | case "password":
274 | data[name] = localeFaker.internet.password();
275 | break;
276 | case "domainName":
277 | data[name] = localeFaker.internet.domainName();
278 | break;
279 | case "protocol":
280 | data[name] = localeFaker.internet.protocol();
281 | break;
282 | case "httpMethod":
283 | data[name] = localeFaker.internet.httpMethod();
284 | break;
285 | case "countryCode":
286 | data[name] = localeFaker.location.countryCode();
287 | break;
288 | case "state":
289 | data[name] = localeFaker.location.state();
290 | break;
291 | case "stateAbbr":
292 | data[name] = localeFaker.location.state({ abbreviated: true });
293 | break;
294 | case "latitude":
295 | data[name] = localeFaker.location.latitude();
296 | break;
297 | case "longitude":
298 | data[name] = localeFaker.location.longitude();
299 | break;
300 | case "timeZone":
301 | data[name] = localeFaker.location.timeZone();
302 | break;
303 | case "weekday":
304 | data[name] = localeFaker.date.weekday();
305 | break;
306 | case "month":
307 | data[name] = localeFaker.date.month();
308 | break;
309 | case "timestamp":
310 | data[name] = localeFaker.date.anytime().getTime();
311 | break;
312 | case "product":
313 | data[name] = localeFaker.commerce.product();
314 | break;
315 | case "productName":
316 | data[name] = localeFaker.commerce.productName();
317 | break;
318 | case "price":
319 | data[name] = localeFaker.commerce.price({
320 | min: options.min || 1,
321 | max: options.max || 1000,
322 | });
323 | break;
324 | case "department":
325 | data[name] = localeFaker.commerce.department();
326 | break;
327 | case "productMaterial":
328 | data[name] = localeFaker.commerce.productMaterial();
329 | break;
330 | case "productDescription":
331 | data[name] = localeFaker.commerce.productDescription();
332 | break;
333 | case "companyName":
334 | data[name] = localeFaker.company.name();
335 | break;
336 | case "catchPhrase":
337 | data[name] = localeFaker.company.catchPhrase();
338 | break;
339 | case "bs":
340 | data[name] = localeFaker.company.buzzPhrase();
341 | break;
342 | case "bsAdjective":
343 | data[name] = localeFaker.company.buzzAdjective();
344 | break;
345 | case "bsBuzz":
346 | data[name] = localeFaker.company.buzzVerb();
347 | break;
348 | case "bsNoun":
349 | data[name] = localeFaker.company.buzzNoun();
350 | break;
351 | case "accountNumber":
352 | data[name] = localeFaker.finance.accountNumber();
353 | break;
354 | case "accountName":
355 | data[name] = localeFaker.finance.accountName();
356 | break;
357 | case "amount":
358 | data[name] = localeFaker.finance.amount();
359 | break;
360 | case "currencyCode":
361 | data[name] = localeFaker.finance.currencyCode();
362 | break;
363 | case "currencyName":
364 | data[name] = localeFaker.finance.currencyName();
365 | break;
366 | case "currencySymbol":
367 | data[name] = localeFaker.finance.currencySymbol();
368 | break;
369 | case "bitcoinAddress":
370 | data[name] = localeFaker.finance.bitcoinAddress();
371 | break;
372 | case "vehicle":
373 | data[name] = localeFaker.vehicle.vehicle();
374 | break;
375 | case "manufacturer":
376 | data[name] = localeFaker.vehicle.manufacturer();
377 | break;
378 | case "model":
379 | data[name] = localeFaker.vehicle.model();
380 | break;
381 | case "type":
382 | data[name] = localeFaker.vehicle.type();
383 | break;
384 | case "fuel":
385 | data[name] = localeFaker.vehicle.fuel();
386 | break;
387 | case "vin":
388 | data[name] = localeFaker.vehicle.vin();
389 | break;
390 | case "fileName":
391 | data[name] = localeFaker.system.fileName();
392 | break;
393 | case "mimeType":
394 | data[name] = localeFaker.system.mimeType();
395 | break;
396 | case "fileExt":
397 | data[name] = localeFaker.system.fileExt();
398 | break;
399 | case "directoryPath":
400 | data[name] = localeFaker.system.directoryPath();
401 | break;
402 | case "semver":
403 | data[name] = localeFaker.system.semver();
404 | break;
405 | case "chemicalElement":
406 | data[name] = localeFaker.science.chemicalElement();
407 | break;
408 | case "unit":
409 | data[name] = localeFaker.science.unit();
410 | break;
411 | case "scientificUnit":
412 | data[name] = localeFaker.science.unit();
413 | break;
414 | case "genre":
415 | data[name] = localeFaker.music.genre();
416 | break;
417 | case "songName":
418 | data[name] = localeFaker.music.songName();
419 | break;
420 | case "artist":
421 | data[name] = localeFaker.music.artist();
422 | break;
423 | case "float":
424 | data[name] = localeFaker.number.float({
425 | min: options.min || 0,
426 | max: options.max || 1000,
427 | fractionDigits: options.precision || 2
428 | });
429 | break;
430 | case "words":
431 | data[name] = localeFaker.word.words();
432 | break;
433 | case "text":
434 | data[name] = localeFaker.lorem.text();
435 | break;
436 | default:
437 | throw new Error(`Unsupported field type: ${type}`);
438 | }
439 | } catch (error) {
440 | console.error(chalk.yellow(`Error generating field ${name}: ${error}`));
441 | data[name] = null;
442 | }
443 | });
444 |
445 | return data;
446 | }
447 |
448 | private generatePersonData(locale: string, fields: string[]) {
449 | const data: Record<string, any> = {};
450 | const localeFaker = createLocaleFaker(locale);
451 |
452 | fields.forEach(field => {
453 | switch (field) {
454 | case "firstName":
455 | data.firstName = localeFaker.person.firstName();
456 | break;
457 | case "lastName":
458 | data.lastName = localeFaker.person.lastName();
459 | break;
460 | case "email":
461 | data.email = localeFaker.internet.email();
462 | break;
463 | case "phone":
464 | data.phone = localeFaker.phone.number();
465 | break;
466 | case "address":
467 | data.address = {
468 | street: localeFaker.location.streetAddress(),
469 | city: localeFaker.location.city(),
470 | state: localeFaker.location.state(),
471 | country: localeFaker.location.country(),
472 | zipCode: localeFaker.location.zipCode()
473 | };
474 | break;
475 | case "dateOfBirth":
476 | data.dateOfBirth = localeFaker.date.past({ years: 70 }).toISOString();
477 | break;
478 | }
479 | });
480 |
481 | return data;
482 | }
483 |
484 | private generateCompanyData(locale: string, fields: string[]) {
485 | const data: Record<string, any> = {};
486 | const localeFaker = createLocaleFaker(locale);
487 |
488 | fields.forEach(field => {
489 | switch (field) {
490 | case "name":
491 | data.name = localeFaker.company.name();
492 | break;
493 | case "industry":
494 | data.industry = localeFaker.company.buzzPhrase();
495 | break;
496 | case "catchPhrase":
497 | data.catchPhrase = localeFaker.company.catchPhrase();
498 | break;
499 | case "address":
500 | data.address = {
501 | street: localeFaker.location.streetAddress(),
502 | city: localeFaker.location.city(),
503 | state: localeFaker.location.state(),
504 | country: localeFaker.location.country(),
505 | zipCode: localeFaker.location.zipCode()
506 | };
507 | break;
508 | case "phone":
509 | data.phone = localeFaker.phone.number();
510 | break;
511 | }
512 | });
513 |
514 | return data;
515 | }
516 |
517 | public processRequest(toolName: string, args: any): { content: Array<{ type: string; text: string }>; isError?: boolean } {
518 | try {
519 | let result;
520 | const locale = args.locale || "en";
521 |
522 | switch (toolName) {
523 | case "generateCustomData":
524 | result = this.generateCustomData(locale, args.fields);
525 | break;
526 | case "generatePerson":
527 | result = this.generatePersonData(locale, args.fields);
528 | break;
529 | case "generateCompany":
530 | result = this.generateCompanyData(locale, args.fields);
531 | break;
532 | default:
533 | throw new Error(`Unknown tool: ${toolName}`);
534 | }
535 |
536 | console.error(chalk.green(`Generated ${toolName} data successfully`));
537 |
538 | return {
539 | content: [{
540 | type: "text",
541 | text: JSON.stringify(result, null, 2)
542 | }]
543 | };
544 | } catch (error) {
545 | console.error(chalk.red(`Error generating mock data: ${error}`));
546 | return {
547 | content: [{
548 | type: "text",
549 | text: JSON.stringify({
550 | error: error instanceof Error ? error.message : String(error),
551 | status: 'failed'
552 | }, null, 2)
553 | }],
554 | isError: true
555 | };
556 | }
557 | }
558 | }
559 |
560 | const server = new Server(
561 | {
562 | name: "mock-data-server",
563 | version: "1.0.0",
564 | },
565 | {
566 | capabilities: {
567 | tools: {},
568 | },
569 | }
570 | );
571 |
572 | const mockDataServer = new MockDataServer();
573 |
574 | server.setRequestHandler(ListToolsRequestSchema, async () => ({
575 | tools: MOCK_DATA_TOOLS,
576 | }));
577 |
578 | server.setRequestHandler(CallToolRequestSchema, async (request) => {
579 | return mockDataServer.processRequest(request.params.name, request.params.arguments);
580 | });
581 |
582 | async function runServer() {
583 | const transport = new StdioServerTransport();
584 | await server.connect(transport);
585 | console.error(chalk.blue("Mock Data MCP Server running on stdio"));
586 | }
587 |
588 | runServer().catch((error) => {
589 | console.error(chalk.red("Fatal error running server:", error));
590 | process.exit(1);
591 | });
```