@nebutra/mcp 0.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.
@@ -0,0 +1,138 @@
1
+ #!/usr/bin/env node
2
+
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
6
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
+ import {
8
+ CallToolRequestSchema,
9
+ ListResourcesRequestSchema,
10
+ ListToolsRequestSchema,
11
+ ReadResourceRequestSchema,
12
+ } from "@modelcontextprotocol/sdk/types.js";
13
+
14
+ /**
15
+ * Nebutra MCP Context Server
16
+ * Exposes core project files and state to Cursor / Windsurf allowing AI Agents
17
+ * to instantly understand the Nebutra-Sailor application structure and routing logic.
18
+ */
19
+ class NebutraContextServer {
20
+ private server: Server;
21
+ private projectRoot: string;
22
+
23
+ constructor(projectRoot?: string) {
24
+ this.projectRoot = projectRoot || process.cwd();
25
+
26
+ this.server = new Server(
27
+ {
28
+ name: "@nebutra/context-server",
29
+ version: "0.1.0",
30
+ },
31
+ {
32
+ capabilities: {
33
+ resources: {},
34
+ tools: {},
35
+ },
36
+ },
37
+ );
38
+
39
+ this.setupResourceHandlers();
40
+ this.setupToolHandlers();
41
+
42
+ this.server.onerror = (_error) => {};
43
+ }
44
+
45
+ private setupResourceHandlers() {
46
+ this.server.setRequestHandler(ListResourcesRequestSchema, async () => ({
47
+ resources: [
48
+ {
49
+ uri: "file:///nebutra.config.json",
50
+ name: "Nebutra Configuration",
51
+ mimeType: "application/json",
52
+ description:
53
+ "Global configurations for the Nebutra template including enabled features (auth, payments, i18n, db).",
54
+ },
55
+ {
56
+ uri: "file:///prisma/schema.prisma",
57
+ name: "Database Schema Overview",
58
+ mimeType: "text/plain",
59
+ description: "The core Prisma schema defining the database tables and relationships.",
60
+ },
61
+ ],
62
+ }));
63
+
64
+ this.server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
65
+ const { uri } = request.params;
66
+
67
+ let filePath = "";
68
+ if (uri === "file:///nebutra.config.json") {
69
+ filePath = path.join(this.projectRoot, "nebutra.config.json");
70
+ } else if (uri === "file:///prisma/schema.prisma") {
71
+ filePath = path.join(this.projectRoot, "packages/platform/db/prisma/schema.prisma");
72
+ } else {
73
+ throw new Error(`Resource not found: ${uri}`);
74
+ }
75
+
76
+ try {
77
+ const content = fs.readFileSync(filePath, "utf-8");
78
+ return {
79
+ contents: [
80
+ {
81
+ uri,
82
+ mimeType: uri.endsWith(".json") ? "application/json" : "text/plain",
83
+ text: content,
84
+ },
85
+ ],
86
+ };
87
+ } catch {
88
+ throw new Error(`Failed to read the requested resource file at ${filePath}`);
89
+ }
90
+ });
91
+ }
92
+
93
+ private setupToolHandlers() {
94
+ this.server.setRequestHandler(ListToolsRequestSchema, async () => ({
95
+ tools: [
96
+ {
97
+ name: "get_project_structure",
98
+ description: "Retrieves the core Next.js routing tree from the apps folder.",
99
+ inputSchema: {
100
+ type: "object",
101
+ properties: {},
102
+ required: [],
103
+ },
104
+ },
105
+ ],
106
+ }));
107
+
108
+ this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
109
+ if (request.params.name === "get_project_structure") {
110
+ try {
111
+ const appsDir = path.join(this.projectRoot, "apps");
112
+ if (!fs.existsSync(appsDir))
113
+ return { content: [{ type: "text", text: "Apps dir not found." }] };
114
+ const dirs = fs.readdirSync(appsDir);
115
+ return { content: [{ type: "text", text: `Apps: ${dirs.join(", ")}` }] };
116
+ } catch (error) {
117
+ return { content: [{ type: "text", text: `Error: ${error}` }], isError: true };
118
+ }
119
+ }
120
+ throw new Error(`Unknown tool: ${request.params.name}`);
121
+ });
122
+ }
123
+
124
+ async run() {
125
+ const transport = new StdioServerTransport();
126
+ await this.server.connect(transport);
127
+ }
128
+ }
129
+
130
+ // Export a factory or run directly if invoked as script
131
+ export const startContextServer = () => {
132
+ const server = new NebutraContextServer();
133
+ server.run().catch(console.error);
134
+ };
135
+
136
+ if (process.argv[1]?.endsWith("contextServer.js") || process.argv[1]?.endsWith("nebutra-mcp")) {
137
+ startContextServer();
138
+ }
@@ -0,0 +1,5 @@
1
+ export {
2
+ getInternalServerIds,
3
+ INTERNAL_SERVERS,
4
+ registerInternalServers,
5
+ } from "./internalServers";
@@ -0,0 +1,70 @@
1
+ import { serverRegistry } from "../registry/serverRegistry";
2
+ import type { MCPServerConfig } from "../types";
3
+
4
+ /**
5
+ * Register internal Nebutra services as MCP servers
6
+ * These wrap our existing microservices with MCP-compatible interfaces
7
+ */
8
+
9
+ export const INTERNAL_SERVERS: MCPServerConfig[] = [
10
+ // AI Service
11
+ {
12
+ id: "nebutra-ai",
13
+ name: "Nebutra AI Service",
14
+ description: "AI-powered text generation, embedding, and translation",
15
+ endpoint: process.env.AI_SERVICE_URL || "http://localhost:8001",
16
+ transport: "http",
17
+ tools: [
18
+ {
19
+ name: "generate_text",
20
+ description: "Generate text using LLM",
21
+ parameters: {
22
+ prompt: { type: "string", description: "Input prompt", required: true },
23
+ max_tokens: { type: "number", description: "Max tokens to generate", default: 1000 },
24
+ temperature: { type: "number", description: "Sampling temperature", default: 0.7 },
25
+ },
26
+ returns: "Generated text",
27
+ },
28
+ {
29
+ name: "create_embedding",
30
+ description: "Create vector embedding for text",
31
+ parameters: {
32
+ text: { type: "string", description: "Text to embed", required: true },
33
+ model: {
34
+ type: "string",
35
+ description: "Embedding model",
36
+ default: "text-embedding-3-small",
37
+ },
38
+ },
39
+ returns: "Vector embedding array",
40
+ },
41
+ {
42
+ name: "translate",
43
+ description: "Translate text between languages",
44
+ parameters: {
45
+ text: { type: "string", description: "Text to translate", required: true },
46
+ source: { type: "string", description: "Source language code", default: "en" },
47
+ target: { type: "string", description: "Target language code", required: true },
48
+ },
49
+ returns: "Translated text",
50
+ },
51
+ ],
52
+ rateLimit: { maxRequests: 100, windowMs: 60000 },
53
+ },
54
+ ];
55
+
56
+ /**
57
+ * Register all internal servers
58
+ */
59
+ export function registerInternalServers(): void {
60
+ for (const server of INTERNAL_SERVERS) {
61
+ serverRegistry.register(server);
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Get list of internal server IDs
67
+ */
68
+ export function getInternalServerIds(): string[] {
69
+ return INTERNAL_SERVERS.map((s) => s.id);
70
+ }
package/src/types.ts ADDED
@@ -0,0 +1,118 @@
1
+ import { z } from "zod";
2
+
3
+ // ============================================
4
+ // MCP Tool Definition
5
+ // ============================================
6
+
7
+ export const ToolParameterSchema = z.object({
8
+ type: z.enum(["string", "number", "boolean", "object", "array"]),
9
+ description: z.string().optional(),
10
+ required: z.boolean().optional(),
11
+ default: z.any().optional(),
12
+ });
13
+
14
+ export const ToolDefinitionSchema = z.object({
15
+ name: z.string(),
16
+ description: z.string(),
17
+ parameters: z.record(z.string(), ToolParameterSchema).optional(),
18
+ returns: z.string().optional(),
19
+ });
20
+
21
+ export type ToolParameter = z.infer<typeof ToolParameterSchema>;
22
+ export type ToolDefinition = z.infer<typeof ToolDefinitionSchema>;
23
+
24
+ // ============================================
25
+ // MCP Request/Response
26
+ // ============================================
27
+
28
+ export interface MCPRequest {
29
+ id: string;
30
+ method: string;
31
+ params?: Record<string, unknown>;
32
+ }
33
+
34
+ export interface MCPResponse {
35
+ id: string;
36
+ result?: unknown;
37
+ error?: MCPError;
38
+ }
39
+
40
+ export interface MCPError {
41
+ code: number;
42
+ message: string;
43
+ data?: unknown;
44
+ }
45
+
46
+ // ============================================
47
+ // MCP Context (with tenant info)
48
+ // ============================================
49
+
50
+ export interface MCPContext {
51
+ requestId: string;
52
+ tenantId?: string;
53
+ userId?: string;
54
+ plan?: string;
55
+ permissions?: string[];
56
+ metadata?: Record<string, unknown>;
57
+ }
58
+
59
+ // ============================================
60
+ // Tool Execution
61
+ // ============================================
62
+
63
+ export interface ToolExecutionRequest {
64
+ tool: string;
65
+ arguments: Record<string, unknown>;
66
+ context: MCPContext;
67
+ }
68
+
69
+ export interface ToolExecutionResult {
70
+ success: boolean;
71
+ result?: unknown;
72
+ error?: string;
73
+ duration: number;
74
+ tokensUsed?: number;
75
+ }
76
+
77
+ // ============================================
78
+ // MCP Server Registration
79
+ // ============================================
80
+
81
+ export interface MCPServerConfig {
82
+ id: string;
83
+ name: string;
84
+ description: string;
85
+ endpoint: string;
86
+ transport: "http" | "websocket" | "stdio";
87
+ tools: ToolDefinition[];
88
+ authentication?: {
89
+ type: "bearer" | "api-key" | "none";
90
+ headerName?: string;
91
+ };
92
+ rateLimit?: {
93
+ maxRequests: number;
94
+ windowMs: number;
95
+ };
96
+ // Tenant access control
97
+ allowedPlans?: string[]; // e.g., ["PRO", "ENTERPRISE"]
98
+ allowedTenants?: string[]; // specific tenant IDs
99
+ }
100
+
101
+ // ============================================
102
+ // Smithery Integration
103
+ // ============================================
104
+
105
+ export interface SmitheryServerInfo {
106
+ qualifiedName: string;
107
+ displayName: string;
108
+ description: string;
109
+ homepage?: string;
110
+ useCount?: number;
111
+ isDeployed?: boolean;
112
+ }
113
+
114
+ export interface SmitheryConfig {
115
+ apiKey?: string;
116
+ baseUrl: string;
117
+ defaultServers?: string[]; // Pre-configured server qualified names
118
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": "src"
6
+ },
7
+ "include": ["src/**/*"],
8
+ "exclude": ["node_modules", "dist"]
9
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,11 @@
1
+ import { defineConfig } from "tsup";
2
+
3
+ export default defineConfig({
4
+ entry: ["src/index.ts", "src/server/contextServer.ts"],
5
+ format: ["esm"],
6
+ dts: true,
7
+ clean: true,
8
+ target: "node18",
9
+ outDir: "dist",
10
+ minify: false,
11
+ });