mcp-agentic-pipelines 1.0.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.
Files changed (119) hide show
  1. package/.env.example +93 -0
  2. package/README.md +258 -0
  3. package/package.json +70 -0
  4. package/packages/clinical/package.json +22 -0
  5. package/packages/clinical/src/index.ts +262 -0
  6. package/packages/clinical/tsconfig.json +13 -0
  7. package/packages/core/package.json +21 -0
  8. package/packages/core/src/config.ts +138 -0
  9. package/packages/core/src/errors.ts +100 -0
  10. package/packages/core/src/index.ts +104 -0
  11. package/packages/core/src/llm-config.ts +213 -0
  12. package/packages/core/src/logging.ts +66 -0
  13. package/packages/core/src/python-bridge.ts +384 -0
  14. package/packages/core/src/rate-limiter.ts +136 -0
  15. package/packages/core/src/types.ts +203 -0
  16. package/packages/core/src/validation.ts +101 -0
  17. package/packages/core/tsconfig.json +10 -0
  18. package/packages/deeppipe/package.json +21 -0
  19. package/packages/deeppipe/src/index.ts +424 -0
  20. package/packages/deeppipe/tsconfig.json +13 -0
  21. package/packages/piste/package.json +20 -0
  22. package/packages/piste/src/index.ts +48 -0
  23. package/packages/piste/tsconfig.json +13 -0
  24. package/packages/precis/package.json +20 -0
  25. package/packages/precis/src/index.ts +67 -0
  26. package/packages/precis/tsconfig.json +13 -0
  27. package/packages/server/package.json +31 -0
  28. package/packages/server/src/index.ts +427 -0
  29. package/packages/server/tsconfig.json +17 -0
  30. package/setup.mjs +141 -0
  31. package/test.mjs +337 -0
  32. package/vendors/clinical-intake/pipeline.mjs +349 -0
  33. package/vendors/clinical-intake/questions/en.txt +9 -0
  34. package/vendors/clinical-intake/questions/fr.txt +9 -0
  35. package/vendors/piste/.env.example +73 -0
  36. package/vendors/piste/app/core/__init__.py +4 -0
  37. package/vendors/piste/app/core/config.py +83 -0
  38. package/vendors/piste/app/core/debuglog.py +16 -0
  39. package/vendors/piste/app/core/middleware.py +40 -0
  40. package/vendors/piste/bridge_piste.py +301 -0
  41. package/vendors/piste/pipeline/__init__.py +4 -0
  42. package/vendors/piste/pipeline/compiler.py +68 -0
  43. package/vendors/piste/pipeline/offline/__init__.py +28 -0
  44. package/vendors/piste/pipeline/offline/verifaid_pipeline.py +247 -0
  45. package/vendors/piste/pipeline/replay.py +15 -0
  46. package/vendors/piste/pipeline/replay_engine.py +249 -0
  47. package/vendors/piste/pipeline/signatures/__init__.py +4 -0
  48. package/vendors/piste/pipeline/signatures/signatures.py +136 -0
  49. package/vendors/piste/pipeline/stage1/__init__.py +21 -0
  50. package/vendors/piste/pipeline/stage1/atomic_decomposer.py +61 -0
  51. package/vendors/piste/pipeline/stage1/check_worthiness.py +100 -0
  52. package/vendors/piste/pipeline/stage1/orchestrator.py +175 -0
  53. package/vendors/piste/pipeline/stage1/test_stage1.py +162 -0
  54. package/vendors/piste/pipeline/stage2/__init__.py +34 -0
  55. package/vendors/piste/pipeline/stage2/blind_retriever.py +303 -0
  56. package/vendors/piste/pipeline/stage2/canonical_mapper.py +124 -0
  57. package/vendors/piste/pipeline/stage2/credibility_scorer.py +85 -0
  58. package/vendors/piste/pipeline/stage2/orchestrator.py +311 -0
  59. package/vendors/piste/pipeline/stage2/query_refiner.py +88 -0
  60. package/vendors/piste/pipeline/stage2/search_decision.py +69 -0
  61. package/vendors/piste/pipeline/stage2/test_stage2.py +265 -0
  62. package/vendors/piste/pipeline/stage3/__init__.py +20 -0
  63. package/vendors/piste/pipeline/stage3/classifier.py +79 -0
  64. package/vendors/piste/pipeline/stage3/orchestrator.py +225 -0
  65. package/vendors/piste/pipeline/stage3/test_stage3.py +101 -0
  66. package/vendors/piste/pipeline/stage4/__init__.py +33 -0
  67. package/vendors/piste/pipeline/stage4/criticality_gate.py +177 -0
  68. package/vendors/piste/pipeline/stage4/orchestrator.py +269 -0
  69. package/vendors/piste/pipeline/stage4/test_stage4.py +192 -0
  70. package/vendors/piste/pipeline/stage4/verdict_aggregator.py +157 -0
  71. package/vendors/piste/requirements.txt +53 -0
  72. package/vendors/precis/backend/__init__.py +6 -0
  73. package/vendors/precis/backend/agents/__init__.py +3 -0
  74. package/vendors/precis/backend/agents/data_synthesis.py +105 -0
  75. package/vendors/precis/backend/agents/dist_free_synth.py +97 -0
  76. package/vendors/precis/backend/agents/exact_hash_retriever.py +327 -0
  77. package/vendors/precis/backend/agents/fusion_ranker.py +64 -0
  78. package/vendors/precis/backend/agents/guardrail.py +175 -0
  79. package/vendors/precis/backend/agents/query_expander.py +89 -0
  80. package/vendors/precis/backend/agents/radial_interpol.py +99 -0
  81. package/vendors/precis/backend/agents/report_generator.py +92 -0
  82. package/vendors/precis/backend/agents/semantic_reranker.py +135 -0
  83. package/vendors/precis/backend/agents/stat_anomaly.py +93 -0
  84. package/vendors/precis/backend/agents/vector_index.py +123 -0
  85. package/vendors/precis/backend/agents/veri_score.py +341 -0
  86. package/vendors/precis/backend/agents/work_order_extractor.py +205 -0
  87. package/vendors/precis/backend/api/__init__.py +3 -0
  88. package/vendors/precis/backend/api/routes/__init__.py +3 -0
  89. package/vendors/precis/backend/config.py +88 -0
  90. package/vendors/precis/backend/core/__init__.py +13 -0
  91. package/vendors/precis/backend/core/hashing.py +22 -0
  92. package/vendors/precis/backend/core/metrics.py +77 -0
  93. package/vendors/precis/backend/core/multitoken.py +166 -0
  94. package/vendors/precis/backend/core/pmi.py +54 -0
  95. package/vendors/precis/backend/core/stemming.py +74 -0
  96. package/vendors/precis/backend/core/tracing.py +150 -0
  97. package/vendors/precis/backend/data/__init__.py +3 -0
  98. package/vendors/precis/backend/data/chunker.py +57 -0
  99. package/vendors/precis/backend/data/pdf_parser.py +42 -0
  100. package/vendors/precis/backend/db/__init__.py +3 -0
  101. package/vendors/precis/backend/db/models.py +173 -0
  102. package/vendors/precis/backend/db/repository.py +269 -0
  103. package/vendors/precis/backend/llm/__init__.py +3 -0
  104. package/vendors/precis/backend/llm/anthropic_provider.py +39 -0
  105. package/vendors/precis/backend/llm/base.py +147 -0
  106. package/vendors/precis/backend/llm/deepseek_provider.py +43 -0
  107. package/vendors/precis/backend/llm/factory.py +60 -0
  108. package/vendors/precis/backend/llm/google_provider.py +39 -0
  109. package/vendors/precis/backend/llm/ollama_provider.py +54 -0
  110. package/vendors/precis/backend/llm/openai_provider.py +50 -0
  111. package/vendors/precis/backend/main.py +677 -0
  112. package/vendors/precis/backend/orchestrator/__init__.py +3 -0
  113. package/vendors/precis/backend/orchestrator/planner.py +81 -0
  114. package/vendors/precis/backend/orchestrator/router.py +319 -0
  115. package/vendors/precis/backend/orchestrator/types.py +58 -0
  116. package/vendors/precis/bridge_precis.py +185 -0
  117. package/vendors/precis/data/sample_reports/README.md +8 -0
  118. package/vendors/precis/data/seed_data.py +115 -0
  119. package/vendors/precis/requirements.txt +19 -0
@@ -0,0 +1,427 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * MCP Agentic Pipelines — Main Entry Point
4
+ *
5
+ * Integrates piste, clinical-intake, precis-agentic-pipeline,
6
+ * DeepPipe, and @kordabjinan/deeppipe into a single MCP server.
7
+ *
8
+ * Transport: stdio (primary) | SSE (secondary)
9
+ * LLM: Multi-provider — OpenAI, Anthropic, Google, DeepSeek, Groq, Ollama, OpenRouter, Azure, Custom
10
+ */
11
+
12
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
13
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
14
+ import {
15
+ CallToolRequestSchema,
16
+ ListToolsRequestSchema,
17
+ ListResourcesRequestSchema,
18
+ ListPromptsRequestSchema,
19
+ ReadResourceRequestSchema,
20
+ GetPromptRequestSchema,
21
+ } from '@modelcontextprotocol/sdk/types.js';
22
+
23
+ import {
24
+ loadConfig,
25
+ createLogger,
26
+ createRateLimiter,
27
+ listProviders,
28
+ PROVIDER_DEFAULTS,
29
+ PythonServiceManager,
30
+ LLMNotConfiguredError,
31
+ InternalError,
32
+ type Config,
33
+ type Logger,
34
+ type RateLimiter,
35
+ type ToolDefinition,
36
+ type ResourceDefinition,
37
+ type PromptDefinition,
38
+ } from '@unified-mcp/core';
39
+
40
+ // ═══════════════════════════════════════════════════════════════════════
41
+ // Bootstrap
42
+ // ═══════════════════════════════════════════════════════════════════════
43
+
44
+ const config: Config = loadConfig();
45
+ const logger: Logger = createLogger(config.MCP_LOG_LEVEL);
46
+ const rateLimiter: RateLimiter = createRateLimiter(config.MCP_RATE_LIMIT_ENABLED, config.MCP_RATE_LIMIT_MAX_RPS);
47
+ const pythonManager = new PythonServiceManager(logger);
48
+
49
+ logger.info(`MCP Agentic Pipelines v${config.MCP_SERVER_VERSION} starting...`);
50
+ logger.info(`Transport: ${config.MCP_TRANSPORT}`);
51
+ logger.info(`LLM Default Provider: ${config.LLM_DEFAULT_PROVIDER}`);
52
+ logger.info(`Log level: ${config.MCP_LOG_LEVEL}`);
53
+
54
+ // ═══════════════════════════════════════════════════════════════════════
55
+ // Tool, Resource, Prompt registries
56
+ // ═══════════════════════════════════════════════════════════════════════
57
+
58
+ const tools: ToolDefinition[] = [];
59
+ const resources: ResourceDefinition[] = [];
60
+ const prompts: PromptDefinition[] = [];
61
+
62
+ // Tool handler map: toolName → handler function
63
+ const toolHandlers = new Map<string, (args: unknown) => Promise<{ content: Array<{ type: 'text'; text: string }>; isError?: boolean }>>();
64
+
65
+ // Resource handler map: uri pattern → handler
66
+ const resourceHandlers = new Map<string, (uri: string) => Promise<{ contents: Array<{ uri: string; mimeType: string; text?: string }> }>>();
67
+
68
+ // Prompt handler map: promptName → handler
69
+ const promptHandlers = new Map<string, (args?: Record<string, string>) => Promise<{ messages: Array<{ role: 'user' | 'assistant'; content: { type: 'text'; text: string } }> }>>();
70
+
71
+ // ═══════════════════════════════════════════════════════════════════════
72
+ // Built-in tools (always available)
73
+ // ═══════════════════════════════════════════════════════════════════════
74
+
75
+ // Tool: mcp_health — check server and integration status
76
+ tools.push({
77
+ name: 'mcp_health',
78
+ description: 'Check the health status of the MCP server and all integration packages. Returns provider configuration, available tools count, and service reachability.',
79
+ inputSchema: { type: 'object', properties: {} },
80
+ });
81
+
82
+ toolHandlers.set('mcp_health', async () => {
83
+ const health = {
84
+ server: {
85
+ name: config.MCP_SERVER_NAME,
86
+ version: config.MCP_SERVER_VERSION,
87
+ transport: config.MCP_TRANSPORT,
88
+ uptime: process.uptime(),
89
+ },
90
+ tools: {
91
+ total: tools.length,
92
+ registered: Array.from(toolHandlers.keys()),
93
+ },
94
+ resources: resources.length,
95
+ prompts: prompts.length,
96
+ llm: {
97
+ defaultProvider: config.LLM_DEFAULT_PROVIDER,
98
+ deepPipe: {
99
+ provider: config.deepPipeLLM.provider,
100
+ model: config.deepPipeLLM.model,
101
+ configured: !!config.deepPipeLLM.apiKey,
102
+ },
103
+ piste: {
104
+ provider: config.pisteLLM.provider,
105
+ model: config.pisteLLM.model,
106
+ configured: !!config.pisteLLM.apiKey,
107
+ },
108
+ precis: {
109
+ provider: config.precisLLM.provider,
110
+ model: config.precisLLM.model,
111
+ configured: !!config.precisLLM.apiKey,
112
+ },
113
+ clinical: {
114
+ provider: config.clinicalLLM.provider,
115
+ model: config.clinicalLLM.model,
116
+ configured: !!config.clinicalLLM.apiKey,
117
+ },
118
+ },
119
+ services: {
120
+ piste: { configured: !!config.PISTE_API_URL },
121
+ precis: { configured: !!config.PRECIS_API_URL },
122
+ clinical: {
123
+ stt: { provider: config.CLINICAL_STT_PROVIDER, configured: !!config.GROQ_API_KEY },
124
+ tts: { provider: config.CLINICAL_TTS_PROVIDER, configured: !!config.ELEVENLABS_API_KEY },
125
+ },
126
+ deepPipe: { indexPath: config.DEEPPIPE_INDEX_PATH },
127
+ },
128
+ };
129
+
130
+ return { content: [{ type: 'text' as const, text: JSON.stringify(health, null, 2) }] };
131
+ });
132
+
133
+ // Tool: mcp_list_providers — list all supported LLM providers with defaults
134
+ tools.push({
135
+ name: 'mcp_list_providers',
136
+ description: 'List all supported LLM providers with their default models and base URLs. Use this to see which providers are available for configuration.',
137
+ inputSchema: {
138
+ type: 'object',
139
+ properties: {
140
+ filter: {
141
+ type: 'string',
142
+ description: 'Optional: filter providers by name substring (e.g. "open" matches openai, openrouter)',
143
+ },
144
+ },
145
+ },
146
+ });
147
+
148
+ toolHandlers.set('mcp_list_providers', async (args: unknown) => {
149
+ const filter = typeof (args as any)?.filter === 'string' ? (args as any).filter.toLowerCase() : '';
150
+
151
+ const providers = listProviders()
152
+ .filter((p) => !filter || p.includes(filter))
153
+ .map((provider) => {
154
+ const defaults = PROVIDER_DEFAULTS[provider as keyof typeof PROVIDER_DEFAULTS];
155
+ const isDefault = provider === config.LLM_DEFAULT_PROVIDER;
156
+
157
+ // Check which components are using this provider
158
+ const usedBy: string[] = [];
159
+ if (config.deepPipeLLM.provider === provider) usedBy.push('deeppipe');
160
+ if (config.pisteLLM.provider === provider) usedBy.push('piste');
161
+ if (config.precisLLM.provider === provider) usedBy.push('precis');
162
+ if (config.clinicalLLM.provider === provider) usedBy.push('clinical');
163
+
164
+ return {
165
+ provider,
166
+ defaultModel: defaults.defaultModel,
167
+ baseUrl: defaults.baseUrl || '(user-specified)',
168
+ isDefaultProvider: isDefault,
169
+ usedByComponents: usedBy.length > 0 ? usedBy : ['(none)'],
170
+ howToConfigure: isDefault
171
+ ? `Set LLM_DEFAULT_API_KEY in .env`
172
+ : `Set LLM_DEFAULT_PROVIDER=${provider} and LLM_DEFAULT_API_KEY in .env, or set per-component like DEEPPIPE_LLM_PROVIDER=${provider}`,
173
+ };
174
+ });
175
+
176
+ return { content: [{ type: 'text' as const, text: JSON.stringify(providers, null, 2) }] };
177
+ });
178
+
179
+ // ═══════════════════════════════════════════════════════════════════════
180
+ // Lazy-load integration packages
181
+ // ═══════════════════════════════════════════════════════════════════════
182
+
183
+ async function loadIntegrations(): Promise<void> {
184
+ // Each integration is loaded with a 10s timeout so one can't hang the server.
185
+
186
+ const withTimeout = async <T>(name: string, fn: () => Promise<T>): Promise<T | null> => {
187
+ try {
188
+ return await Promise.race([
189
+ fn(),
190
+ new Promise<null>((_, reject) => setTimeout(() => reject(new Error('TIMEOUT')), 10000)),
191
+ ]);
192
+ } catch (err: any) {
193
+ logger.warn(`Integration skipped: ${name} — ${err?.message || err}`);
194
+ return null;
195
+ }
196
+ };
197
+
198
+ // -- DeepPipe (direct engine import) --
199
+ await withTimeout('deeppipe', async () => {
200
+ const { registerDeepPipe } = await import('@unified-mcp/deeppipe');
201
+ registerDeepPipe({ config, logger, rateLimiter, tools, resources, prompts, toolHandlers, resourceHandlers, promptHandlers });
202
+ logger.info('Integration loaded: deeppipe (engine direct)');
203
+ });
204
+
205
+ // -- Piste (Python bridge to DSPy pipeline) --
206
+ await withTimeout('piste', async () => {
207
+ const { registerPiste } = await import('@unified-mcp/piste');
208
+ registerPiste({ config, logger, rateLimiter, tools, resources, prompts, toolHandlers, resourceHandlers, promptHandlers, pythonManager });
209
+ logger.info('Integration loaded: piste (Python bridge)');
210
+ });
211
+
212
+ // -- Precis (Python bridge to RAG pipeline) --
213
+ await withTimeout('precis', async () => {
214
+ const { registerPrecis } = await import('@unified-mcp/precis');
215
+ registerPrecis({ config, logger, rateLimiter, tools, resources, prompts, toolHandlers, resourceHandlers, promptHandlers, pythonManager });
216
+ logger.info('Integration loaded: precis (Python bridge)');
217
+ });
218
+
219
+ // -- Clinical Intake (native TypeScript pipeline) --
220
+ await withTimeout('clinical', async () => {
221
+ const { registerClinical } = await import('@unified-mcp/clinical');
222
+ registerClinical({ config, logger, rateLimiter, tools, resources, prompts, toolHandlers, resourceHandlers, promptHandlers });
223
+ logger.info('Integration loaded: clinical (native pipeline)');
224
+ });
225
+ }
226
+
227
+ // ═══════════════════════════════════════════════════════════════════════
228
+ // Dependency guard — verifies setup.mjs was run before starting
229
+ // ═══════════════════════════════════════════════════════════════════════
230
+
231
+ import { existsSync } from 'fs';
232
+ import { resolve } from 'path';
233
+
234
+ function verifyDependencies(): void {
235
+ const targetDir = resolve(process.cwd(), '.python-packages');
236
+ if (!existsSync(targetDir)) {
237
+ process.stderr.write('\n⚠ .python-packages/ not found.\n');
238
+ process.stderr.write(' Run: node setup.mjs\n\n');
239
+ return;
240
+ }
241
+ // Quick file-system check — setup.mjs already verified everything
242
+ const keyMods = ['dspy', 'litellm', 'fastapi', 'nltk'];
243
+ const missing = keyMods.filter(m => !existsSync(resolve(targetDir, m)));
244
+ if (missing.length > 0) {
245
+ process.stderr.write(`\n⚠ Missing packages: ${missing.join(', ')}\n`);
246
+ process.stderr.write(' Run: node setup.mjs\n\n');
247
+ }
248
+ }
249
+
250
+ // ═══════════════════════════════════════════════════════════════════════
251
+ // MCP Server Setup
252
+ // ═══════════════════════════════════════════════════════════════════════
253
+
254
+ async function main(): Promise<void> {
255
+ verifyDependencies();
256
+
257
+ // Load all integration packages
258
+ await loadIntegrations();
259
+
260
+ logger.info(`Registered ${tools.length} tools, ${resources.length} resources, ${prompts.length} prompts`);
261
+
262
+ // Create MCP server
263
+ const server = new Server(
264
+ {
265
+ name: config.MCP_SERVER_NAME,
266
+ version: config.MCP_SERVER_VERSION,
267
+ },
268
+ {
269
+ capabilities: {
270
+ tools: {},
271
+ resources: {},
272
+ prompts: {},
273
+ },
274
+ },
275
+ );
276
+
277
+ // ── List Tools ─────────────────────────────────────────────────
278
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
279
+ tools: tools.map((t) => ({
280
+ name: t.name,
281
+ description: t.description,
282
+ inputSchema: t.inputSchema,
283
+ })),
284
+ }));
285
+
286
+ // ── Call Tool ──────────────────────────────────────────────────
287
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
288
+ const { name, arguments: args } = request.params;
289
+ const handler = toolHandlers.get(name);
290
+
291
+ if (!handler) {
292
+ return {
293
+ content: [{ type: 'text' as const, text: JSON.stringify({ code: 'UNKNOWN_TOOL', message: `Tool "${name}" is not registered.` }) }],
294
+ isError: true,
295
+ };
296
+ }
297
+
298
+ try {
299
+ // Rate limit check
300
+ rateLimiter.check(name);
301
+
302
+ logger.debug(`Tool called: ${name}`, name);
303
+ const result = await handler(args ?? {});
304
+ return result;
305
+ } catch (error: any) {
306
+ logger.error(`Tool error: ${name}`, name, error.message);
307
+
308
+ // If it's already an MCPToolError, use its response format
309
+ if (error?.toMCPResponse) {
310
+ return error.toMCPResponse();
311
+ }
312
+
313
+ return new InternalError(error?.message ?? String(error)).toMCPResponse();
314
+ }
315
+ });
316
+
317
+ // ── List Resources ─────────────────────────────────────────────
318
+ server.setRequestHandler(ListResourcesRequestSchema, async () => ({
319
+ resources: resources.map((r) => ({
320
+ uri: r.uri,
321
+ name: r.name,
322
+ description: r.description,
323
+ mimeType: r.mimeType,
324
+ })),
325
+ }));
326
+
327
+ // ── Read Resource ──────────────────────────────────────────────
328
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
329
+ const { uri } = request.params;
330
+
331
+ // Try exact match first, then pattern match
332
+ for (const [pattern, handler] of resourceHandlers) {
333
+ // Convert pattern like 'deeppipe://documents/{id}' to regex
334
+ const regex = new RegExp('^' + pattern.replace(/\{(\w+)\}/g, '([^/]+)') + '$');
335
+ if (regex.test(uri)) {
336
+ try {
337
+ return await handler(uri);
338
+ } catch (error: any) {
339
+ return {
340
+ contents: [{
341
+ uri,
342
+ mimeType: 'application/json',
343
+ text: JSON.stringify({ error: error?.message ?? 'Resource read failed' }),
344
+ }],
345
+ };
346
+ }
347
+ }
348
+ }
349
+
350
+ return {
351
+ contents: [{
352
+ uri,
353
+ mimeType: 'application/json',
354
+ text: JSON.stringify({ error: `Resource not found: ${uri}` }),
355
+ }],
356
+ };
357
+ });
358
+
359
+ // ── List Prompts ───────────────────────────────────────────────
360
+ server.setRequestHandler(ListPromptsRequestSchema, async () => ({
361
+ prompts: prompts.map((p) => ({
362
+ name: p.name,
363
+ description: p.description,
364
+ arguments: p.arguments,
365
+ })),
366
+ }));
367
+
368
+ // ── Get Prompt ─────────────────────────────────────────────────
369
+ server.setRequestHandler(GetPromptRequestSchema, async (request) => {
370
+ const { name, arguments: args } = request.params;
371
+ const handler = promptHandlers.get(name);
372
+
373
+ if (!handler) {
374
+ return {
375
+ messages: [{
376
+ role: 'user' as const,
377
+ content: { type: 'text' as const, text: `Prompt "${name}" not found.` },
378
+ }],
379
+ };
380
+ }
381
+
382
+ try {
383
+ return await handler(args as Record<string, string> | undefined);
384
+ } catch (error: any) {
385
+ return {
386
+ messages: [{
387
+ role: 'user' as const,
388
+ content: { type: 'text' as const, text: `Error loading prompt: ${error?.message ?? error}` },
389
+ }],
390
+ };
391
+ }
392
+ });
393
+
394
+ // ── Connect Transport ──────────────────────────────────────────
395
+ if (config.MCP_TRANSPORT === 'sse') {
396
+ // SSE transport requires a running HTTP server; for self-contained usage, default to stdio
397
+ logger.info('SSE transport not yet configured for standalone mode. Using stdio.');
398
+ const transport = new StdioServerTransport();
399
+ await server.connect(transport);
400
+ logger.info('MCP Server connected via stdio (fallback from SSE)');
401
+ } else {
402
+ const transport = new StdioServerTransport();
403
+ await server.connect(transport);
404
+ logger.info('MCP Server connected via stdio');
405
+ }
406
+
407
+ logger.info('Ready for requests.');
408
+ process.stderr.write('__MCP_READY__\n'); // signal test client / launchers
409
+ }
410
+
411
+ // ── Graceful shutdown ────────────────────────────────────────────────
412
+ process.on('SIGINT', () => {
413
+ logger.info('Shutting down...');
414
+ pythonManager.stopAll();
415
+ process.exit(0);
416
+ });
417
+ process.on('SIGTERM', () => {
418
+ logger.info('Shutting down...');
419
+ pythonManager.stopAll();
420
+ process.exit(0);
421
+ });
422
+
423
+ // ── Start ────────────────────────────────────────────────────────────
424
+ main().catch((err) => {
425
+ logger.error('Fatal startup error', undefined, err);
426
+ process.exit(1);
427
+ });
@@ -0,0 +1,17 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "rootDir": "src",
5
+ "outDir": "dist",
6
+ "composite": true
7
+ },
8
+ "include": ["src/**/*"],
9
+ "exclude": ["dist"],
10
+ "references": [
11
+ { "path": "../core" },
12
+ { "path": "../deeppipe" },
13
+ { "path": "../piste" },
14
+ { "path": "../precis" },
15
+ { "path": "../clinical" }
16
+ ]
17
+ }
package/setup.mjs ADDED
@@ -0,0 +1,141 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * ╔══════════════════════════════════════════════════════════════════╗
4
+ * ║ Unified MCP Server — Setup (uv-powered) ║
5
+ * ║ ║
6
+ * ║ Uses uv (Astral) — the industry standard for MCP servers. ║
7
+ * ║ No Python pre-installed. No pip. No MAX_PATH issues. ║
8
+ * ║ One command: node setup.mjs ║
9
+ * ╚══════════════════════════════════════════════════════════════════╝
10
+ */
11
+ import { execSync } from 'node:child_process';
12
+ import { existsSync, readFileSync, mkdirSync } from 'node:fs';
13
+ import { resolve, dirname } from 'node:path';
14
+ import { fileURLToPath } from 'node:url';
15
+
16
+ const __dirname = dirname(fileURLToPath(import.meta.url));
17
+ const ROOT = resolve(__dirname);
18
+ const UV = resolve(ROOT, '.vendor', process.platform === 'win32' ? 'uv.exe' : 'uv');
19
+ const TARGET = resolve(ROOT, '.python-packages');
20
+ const ENV_FILE = resolve(ROOT, '.env');
21
+
22
+ // ANSI
23
+ const T='\x1b[32m✓\x1b[0m', X='\x1b[31m✗\x1b[0m', W='\x1b[33m⚠\x1b[0m';
24
+ const B='\x1b[1m', D='\x1b[2m', C='\x1b[36m', R='\x1b[0m';
25
+ const HR=D+'─'.repeat(55)+R;
26
+ let fail=false;
27
+ function head(t) { console.log(`\n${C}${B} ${t}${R}\n ${HR}`); }
28
+ function ok(m) { console.log(` ${T} ${m}`); }
29
+ function err(m) { console.log(` ${X} ${m}`); fail=true; }
30
+ function warn(m){ console.log(` ${W} ${m}`); }
31
+ function info(m){ console.log(` ${m}`); }
32
+ // execSync with shell — reliable on all platforms
33
+ function sh(cmd, opts={}) { return execSync(cmd, { cwd:ROOT, stdio:'pipe', encoding:'utf8', timeout:300_000, ...opts }); }
34
+
35
+ console.log(`\n${C}${B} ╔══════════════════════════════════════════════════╗${R}`);
36
+ console.log(`${C}${B} ║ MCP Agentic Pipelines — Setup (uv) ║${R}`);
37
+ console.log(`${C}${B} ║ 5 integrations · 31 tools · One command ║${R}`);
38
+ console.log(`${C}${B} ╚══════════════════════════════════════════════════╝${R}`);
39
+
40
+ // 1. Node.js
41
+ head('Node.js Runtime');
42
+ const nodeV = process.version;
43
+ if (parseInt(nodeV.slice(1)) >= 18) ok(`Node.js ${nodeV}`);
44
+ else { err(`Need Node.js 18+. Found ${nodeV}`); process.exit(1); }
45
+
46
+ // 2. npm
47
+ head('Node.js Packages (npm)');
48
+ try { sh('node -e "require.resolve(\\"@unified-mcp/core\\")"'); ok('npm ready'); }
49
+ catch {
50
+ info('Running npm install...');
51
+ try { sh('npm install --prefer-offline', { stdio:'inherit' }); ok('npm complete'); }
52
+ catch(e) { err(e.message); process.exit(1); }
53
+ }
54
+
55
+ // 3. uv
56
+ head('Package Manager (uv)');
57
+ if (!existsSync(UV)) {
58
+ err('.vendor/uv.exe not found. Download it:');
59
+ info('https://github.com/astral-sh/uv/releases/latest');
60
+ info('Place uv.exe in .vendor/ and re-run.');
61
+ process.exit(1);
62
+ }
63
+ ok(`uv ${sh(`"${UV}" --version`).trim()}`);
64
+
65
+ // 3b. Ensure proper Python (uv managed — no Store Python SSL issues)
66
+ head('Python Runtime');
67
+ const isStorePython = (() => {
68
+ try {
69
+ const pyPath = sh(`"${UV}" python find 3.11`).trim();
70
+ return pyPath.includes('WindowsApps'); // Microsoft Store Python has broken SSL
71
+ } catch { return false; }
72
+ })();
73
+
74
+ if (isStorePython) {
75
+ warn('Microsoft Store Python detected — SSL is broken (sandboxed).');
76
+ info('Installing uv-managed Python 3.11 (proper SSL, no sandbox)...');
77
+ info(' This downloads ~25 MB — may take a minute on slow connections.');
78
+ try {
79
+ sh(`"${UV}" python install 3.11`, { stdio:'inherit', timeout: 180_000 });
80
+ const managed = sh(`"${UV}" python find 3.11`).trim();
81
+ ok(`uv-managed Python ready → ${managed}`);
82
+ } catch(e) {
83
+ warn(`uv-managed Python install failed: ${e.message.split('\n')[0]}`);
84
+ info('Python tools (piste, precis) will be unavailable.');
85
+ info('Fix: install python.org Python from https://python.org');
86
+ info('Then re-run: node setup.mjs');
87
+ // Non-fatal — npm-based tools still work
88
+ }
89
+ } else {
90
+ const pyPath = sh(`"${UV}" python find 3.11`).trim();
91
+ ok(`Python 3.11 → ${pyPath}`);
92
+ }
93
+
94
+ // 4. Python packages
95
+ head('Python Packages (uv pip)');
96
+ mkdirSync(TARGET, { recursive: true });
97
+ const PKGS = ['dspy-ai','litellm','python-dotenv','fastapi','uvicorn','pydantic','numpy','nltk','sqlalchemy','httpx'];
98
+ const MODS = ['dspy','litellm','dotenv','fastapi','uvicorn','pydantic','numpy','nltk','sqlalchemy','httpx'];
99
+
100
+ info(`Installing ${PKGS.length} packages (uv is 10-100x faster than pip)...`);
101
+ try {
102
+ sh(`"${UV}" pip install ${PKGS.join(' ')} --target "${TARGET}" --python 3.11`, { stdio:'inherit' });
103
+ // Verify by checking file system (avoids Store Python SSL issues)
104
+ let good = true;
105
+ for (const mod of MODS) {
106
+ if (existsSync(resolve(TARGET, mod)) || existsSync(resolve(TARGET, `${mod}.py`))) {
107
+ ok(mod.padEnd(14));
108
+ } else {
109
+ // Some packages install under a different name (e.g. python-dotenv → dotenv)
110
+ const altCheck = [mod, mod.replace(/-/g, '_'), `python_${mod}`];
111
+ const found = altCheck.some(a => existsSync(resolve(TARGET, a)) || existsSync(resolve(TARGET, `${a}.py`)));
112
+ if (found) ok(mod.padEnd(14));
113
+ else { err(mod.padEnd(14)); good = false; }
114
+ }
115
+ }
116
+ if (good) ok(`All ${PKGS.length} packages verified`);
117
+ } catch(e) {
118
+ err(`uv install failed: ${e.message.split('\n')[0]}`);
119
+ }
120
+
121
+ if (fail) process.exit(1);
122
+
123
+ // 5. .env
124
+ head('Environment (.env)');
125
+ const KEYS = ['DEEPSEEK_API_KEY','OPENAI_API_KEY','GROQ_API_KEY','ELEVENLABS_API_KEY','TAVILY_API_KEY','SERPER_API_KEY','GOOGLE_CSE_API_KEY','GOOGLE_CSE_ID'];
126
+ if (existsSync(ENV_FILE)) {
127
+ const env = readFileSync(ENV_FILE, 'utf8'); let n = 0;
128
+ for (const k of KEYS) {
129
+ if (new RegExp(`^${k}=.+`,'m').test(env)) { ok(k.padEnd(22)); n++; }
130
+ else warn(`${k.padEnd(22)}${D}(optional)${R}`);
131
+ }
132
+ info(`\n ${n}/${KEYS.length} keys configured`);
133
+ } else { warn('.env not found — create one with your API keys'); }
134
+
135
+ // Summary
136
+ console.log(`\n${HR}`);
137
+ if (fail) { console.log(`\n${X} Setup failed. Fix and re-run: node setup.mjs\n`); process.exit(1); }
138
+ console.log(`\n${T} ${B}Setup complete — all dependencies ready.${R}\n`);
139
+ console.log(` ${B}Start server:${R} ${C}npx mcp-agentic-pipelines${R}`);
140
+ console.log(` ${B}Run tests:${R} ${C}node test.mjs${R}`);
141
+ console.log(` ${B}MCP config:${R} ${D}{"command":"npx","args":["mcp-agentic-pipelines"]}${R}\n`);