@braid-cloud/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.
package/README.md ADDED
@@ -0,0 +1,176 @@
1
+ # @braid-cloud/mcp
2
+
3
+ braid MCP stdio proxy - enables tools that don't support remote MCP (like Zed) to use braid.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install -g @braid-cloud/mcp
9
+ ```
10
+
11
+ ## Setup
12
+
13
+ 1. Get a Personal Access Token from [braid](https://app.braid.cloud)
14
+ 2. Set the token as an environment variable:
15
+
16
+ ```bash
17
+ export BRAID_TOKEN=br_your_token_here
18
+ ```
19
+
20
+ ## Usage
21
+
22
+ ### Command Line
23
+
24
+ ```bash
25
+ braid-mcp
26
+ ```
27
+
28
+ The proxy runs in stdio mode, accepting MCP requests on stdin and responding on stdout.
29
+
30
+ ### Zed Configuration
31
+
32
+ Add to your Zed settings:
33
+
34
+ ```json
35
+ {
36
+ "context_servers": {
37
+ "braid": {
38
+ "command": {
39
+ "path": "braid-mcp",
40
+ "args": [],
41
+ "env": {
42
+ "BRAID_TOKEN": "br_your_token_here"
43
+ }
44
+ }
45
+ }
46
+ }
47
+ }
48
+ ```
49
+
50
+ ### Claude Desktop Configuration
51
+
52
+ Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
53
+
54
+ ```json
55
+ {
56
+ "mcpServers": {
57
+ "braid": {
58
+ "command": "braid-mcp",
59
+ "env": {
60
+ "BRAID_TOKEN": "br_your_token_here"
61
+ }
62
+ }
63
+ }
64
+ }
65
+ ```
66
+
67
+ ## Environment Variables
68
+
69
+ | Variable | Required | Description |
70
+ | --------------------- | -------- | ------------------------------------------------------------------------ |
71
+ | `BRAID_TOKEN` | Yes\* | Your braid Personal Access Token |
72
+ | `BRAID_MCP_URL` | No | Custom MCP server URL (defaults to `https://app.braid.cloud/api/mcp`) |
73
+
74
+ \* Token can also be provided via `braid.user.json` (see below)
75
+
76
+ ## Repository Configuration
77
+
78
+ The proxy automatically discovers and loads configuration from your repository, enabling project-specific settings.
79
+
80
+ ### Config Files
81
+
82
+ - **`braid.json`** - Committable team config (projects, default profile)
83
+ - **`braid.user.json`** - Gitignored personal overrides (token, personal projects)
84
+
85
+ The proxy searches upward from the current directory to find these files.
86
+
87
+ ### Example: braid.json (commit to repo)
88
+
89
+ ```json
90
+ {
91
+ "$schema": "https://braid.cloud/schemas/config.json",
92
+ "context": {
93
+ "orgId": "org_abc123",
94
+ "orgProjectIds": ["proj_frontend", "proj_shared"],
95
+ "profileName": "web-development"
96
+ }
97
+ }
98
+ ```
99
+
100
+ ### Example: braid.user.json (add to .gitignore)
101
+
102
+ ```json
103
+ {
104
+ "token": "br_your_personal_token",
105
+ "context": {
106
+ "personalProjectIds": ["proj_my_experiments"],
107
+ "profileName": "my-custom-profile"
108
+ }
109
+ }
110
+ ```
111
+
112
+ ### Context Options
113
+
114
+ | Field | Type | Description |
115
+ | -------------------- | -------- | ------------------------------------------------------- |
116
+ | `profileName` | string | Named profile to use (stored in braid) |
117
+ | `orgId` | string | Organization ID to scope searches |
118
+ | `orgProjectIds` | string[] | Org project IDs to include in search |
119
+ | `personalProjectIds` | string[] | Personal project IDs to include |
120
+ | `includeUserGlobal` | boolean | Include user's global rules (default: true) |
121
+ | `includeOrgGlobal` | boolean | Include org's global rules (default: true if orgId set) |
122
+ | `resolveOverlays` | boolean | Auto-resolve personal overlays (default: true) |
123
+
124
+ ### Config Merging
125
+
126
+ User config overrides base config:
127
+
128
+ 1. Fields in `braid.user.json` take precedence
129
+ 2. Token from user config is preferred over env variable
130
+ 3. Context fields are merged (user overrides base)
131
+
132
+ ## Available Tools
133
+
134
+ The proxy forwards all tools from the braid MCP server:
135
+
136
+ - **rules.search** - Search for rules using natural language queries
137
+ - **context.get** - Get current context configuration
138
+ - **projects.list** - List available projects
139
+
140
+ ## How It Works
141
+
142
+ ```
143
+ Your Tool (Zed, Claude Desktop, etc.)
144
+ │ stdio (stdin/stdout)
145
+
146
+ braid-mcp (this package)
147
+ │ HTTP + Bearer token
148
+
149
+ braid MCP Server
150
+
151
+
152
+ braid Backend
153
+ ```
154
+
155
+ The proxy creates a stdio-based MCP server that forwards all requests to the hosted braid MCP server, handling authentication automatically.
156
+
157
+ ## Programmatic Usage
158
+
159
+ You can also use this package programmatically:
160
+
161
+ ```typescript
162
+ import { createProxyServer } from "@braid-cloud/mcp";
163
+
164
+ const { server, client, close } = await createProxyServer({
165
+ token: "br_your_token_here",
166
+ serverUrl: "https://app.braid.cloud/api/mcp", // optional
167
+ repoContext: { profileName: "my-profile" }, // optional
168
+ });
169
+
170
+ // Later, when done:
171
+ await close();
172
+ ```
173
+
174
+ ## License
175
+
176
+ MIT
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/cli.js ADDED
@@ -0,0 +1,353 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/proxy.ts
4
+ import process2 from "process";
5
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
6
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
7
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
8
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
+ import {
10
+ CallToolRequestSchema,
11
+ ListToolsRequestSchema
12
+ } from "@modelcontextprotocol/sdk/types.js";
13
+ import { Data as Data2, Effect as Effect2, pipe as pipe2 } from "effect";
14
+
15
+ // src/config.ts
16
+ import { readFile } from "fs/promises";
17
+ import { dirname, join, resolve } from "path";
18
+ import process from "process";
19
+ import { Data, Effect, pipe } from "effect";
20
+ var CONFIG_FILENAME = "braid.json";
21
+ var USER_CONFIG_FILENAME = "braid.user.json";
22
+ var FileReadError = class extends Data.TaggedError("FileReadError") {
23
+ };
24
+ var readJsonFile = (path) => pipe(
25
+ Effect.tryPromise({
26
+ try: () => readFile(path, "utf-8"),
27
+ catch: () => new FileReadError({})
28
+ }),
29
+ Effect.flatMap(
30
+ (content) => pipe(
31
+ Effect.try(() => JSON.parse(content)),
32
+ Effect.orElseSucceed(() => null)
33
+ )
34
+ ),
35
+ Effect.orElseSucceed(() => null)
36
+ );
37
+ var fileExists = (path) => pipe(
38
+ Effect.tryPromise({
39
+ try: () => readFile(path),
40
+ catch: () => new FileReadError({})
41
+ }),
42
+ Effect.map(() => true),
43
+ Effect.orElseSucceed(() => false)
44
+ );
45
+ var findConfigDir = (startDir) => {
46
+ const search = (currentDir) => {
47
+ const root = dirname(currentDir);
48
+ if (currentDir === root) {
49
+ return Effect.succeed(null);
50
+ }
51
+ const configPath = join(currentDir, CONFIG_FILENAME);
52
+ const userConfigPath = join(currentDir, USER_CONFIG_FILENAME);
53
+ return pipe(
54
+ Effect.all([fileExists(configPath), fileExists(userConfigPath)]),
55
+ Effect.flatMap(
56
+ ([hasConfig, hasUserConfig]) => hasConfig || hasUserConfig ? Effect.succeed(currentDir) : search(dirname(currentDir))
57
+ )
58
+ );
59
+ };
60
+ return search(resolve(startDir));
61
+ };
62
+ var mergeConfigValues = (baseConfig, userConfig) => {
63
+ return {
64
+ orgProjects: userConfig?.orgProjects ?? baseConfig?.orgProjects,
65
+ personalProjects: userConfig?.personalProjects ?? baseConfig?.personalProjects,
66
+ profile: userConfig?.profile ?? baseConfig?.profile,
67
+ org: userConfig?.org ?? baseConfig?.org,
68
+ token: userConfig?.token ?? baseConfig?.token,
69
+ serverUrl: userConfig?.serverUrl ?? baseConfig?.serverUrl,
70
+ includeUserGlobalRules: userConfig?.includeUserGlobalRules ?? baseConfig?.includeUserGlobalRules,
71
+ includeOrgGlobalRules: userConfig?.includeOrgGlobalRules ?? baseConfig?.includeOrgGlobalRules,
72
+ mcpMode: userConfig?.mcpMode ?? baseConfig?.mcpMode
73
+ };
74
+ };
75
+ var filterDefinedValues = (merged, baseConfig, userConfig, configPath, userConfigPath) => {
76
+ const result = {};
77
+ if (merged.orgProjects !== void 0) {
78
+ result.orgProjects = merged.orgProjects;
79
+ }
80
+ if (merged.personalProjects !== void 0) {
81
+ result.personalProjects = merged.personalProjects;
82
+ }
83
+ if (merged.profile !== void 0) {
84
+ result.profile = merged.profile;
85
+ }
86
+ if (merged.org !== void 0) {
87
+ result.org = merged.org;
88
+ }
89
+ if (merged.token !== void 0) {
90
+ result.token = merged.token;
91
+ }
92
+ if (merged.serverUrl !== void 0) {
93
+ result.serverUrl = merged.serverUrl;
94
+ }
95
+ if (merged.includeUserGlobalRules !== void 0) {
96
+ result.includeUserGlobalRules = merged.includeUserGlobalRules;
97
+ }
98
+ if (merged.includeOrgGlobalRules !== void 0) {
99
+ result.includeOrgGlobalRules = merged.includeOrgGlobalRules;
100
+ }
101
+ if (merged.mcpMode !== void 0) {
102
+ result.mcpMode = merged.mcpMode;
103
+ }
104
+ if (baseConfig) {
105
+ result.configPath = configPath;
106
+ }
107
+ if (userConfig) {
108
+ result.userConfigPath = userConfigPath;
109
+ }
110
+ return result;
111
+ };
112
+ var buildResolvedConfig = (baseConfig, userConfig, configPath, userConfigPath) => {
113
+ if (!(baseConfig || userConfig)) {
114
+ return null;
115
+ }
116
+ const merged = mergeConfigValues(baseConfig, userConfig);
117
+ return filterDefinedValues(
118
+ merged,
119
+ baseConfig,
120
+ userConfig,
121
+ configPath,
122
+ userConfigPath
123
+ );
124
+ };
125
+ var loadRepoConfig = (startDir) => {
126
+ const searchDir = startDir ?? process.env.BRAID_CONFIG_DIR ?? process.cwd();
127
+ return pipe(
128
+ findConfigDir(searchDir),
129
+ Effect.flatMap((configDir) => {
130
+ if (!configDir) {
131
+ return Effect.succeed(null);
132
+ }
133
+ const configPath = join(configDir, CONFIG_FILENAME);
134
+ const userConfigPath = join(configDir, USER_CONFIG_FILENAME);
135
+ return pipe(
136
+ Effect.all({
137
+ baseConfig: readJsonFile(configPath),
138
+ userConfig: readJsonFile(userConfigPath)
139
+ }),
140
+ Effect.map(
141
+ ({ baseConfig, userConfig }) => buildResolvedConfig(
142
+ baseConfig,
143
+ userConfig,
144
+ configPath,
145
+ userConfigPath
146
+ )
147
+ )
148
+ );
149
+ })
150
+ );
151
+ };
152
+ var hasContextOverrides = (config) => Boolean(
153
+ config.orgProjects?.length || config.personalProjects?.length || config.profile || config.org
154
+ );
155
+
156
+ // src/proxy.ts
157
+ var ProxyError = class extends Data2.TaggedError("ProxyError") {
158
+ };
159
+ var DEFAULT_SERVER_URL = "https://app.braid.cloud/api/mcp";
160
+ var createMcpClient = (serverUrl, token, mcpMode) => Effect2.tryPromise({
161
+ try: async () => {
162
+ const headers = {
163
+ Authorization: `Bearer ${token}`
164
+ };
165
+ if (mcpMode) {
166
+ headers["X-Braid-Mcp-Mode"] = mcpMode;
167
+ }
168
+ const clientTransport = new StreamableHTTPClientTransport(
169
+ new URL(serverUrl),
170
+ {
171
+ requestInit: {
172
+ headers
173
+ }
174
+ }
175
+ );
176
+ const client = new Client(
177
+ { name: "braid-mcp-proxy", version: "0.1.0" },
178
+ { capabilities: {} }
179
+ );
180
+ await client.connect(clientTransport);
181
+ return client;
182
+ },
183
+ catch: (e) => new ProxyError({
184
+ code: "connection_error",
185
+ message: e instanceof Error ? e.message : "Failed to connect to server"
186
+ })
187
+ });
188
+ var injectContext = (args, repoContext) => {
189
+ if (!(repoContext && hasContextOverrides(repoContext))) {
190
+ return args;
191
+ }
192
+ const result = { ...args };
193
+ if (repoContext.orgProjects !== void 0 && args.orgProjects === void 0) {
194
+ result.orgProjects = repoContext.orgProjects;
195
+ }
196
+ if (repoContext.personalProjects !== void 0 && args.personalProjects === void 0) {
197
+ result.personalProjects = repoContext.personalProjects;
198
+ }
199
+ if (repoContext.profile !== void 0 && args.profile === void 0) {
200
+ result.profile = repoContext.profile;
201
+ }
202
+ if (repoContext.org !== void 0 && args.org === void 0) {
203
+ result.org = repoContext.org;
204
+ }
205
+ if (repoContext.includeUserGlobalRules !== void 0 && args.includeUserGlobalRules === void 0) {
206
+ result.includeUserGlobalRules = repoContext.includeUserGlobalRules;
207
+ }
208
+ if (repoContext.includeOrgGlobalRules !== void 0 && args.includeOrgGlobalRules === void 0) {
209
+ result.includeOrgGlobalRules = repoContext.includeOrgGlobalRules;
210
+ }
211
+ return result;
212
+ };
213
+ var createMcpServer = (client, repoContext) => Effect2.tryPromise({
214
+ try: async () => {
215
+ const serverTransport = new StdioServerTransport();
216
+ const server = new Server(
217
+ { name: "braid", version: "0.1.0" },
218
+ { capabilities: { tools: {} } }
219
+ );
220
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
221
+ const result = await client.listTools();
222
+ return result;
223
+ });
224
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
225
+ const injectedArgs = injectContext(
226
+ request.params.arguments ?? {},
227
+ repoContext
228
+ );
229
+ const result = await client.callTool({
230
+ name: request.params.name,
231
+ arguments: injectedArgs
232
+ });
233
+ return result;
234
+ });
235
+ await server.connect(serverTransport);
236
+ return server;
237
+ },
238
+ catch: (e) => new ProxyError({
239
+ code: "runtime_error",
240
+ message: e instanceof Error ? e.message : "Failed to create server"
241
+ })
242
+ });
243
+ var createProxyServer = (config) => {
244
+ const serverUrl = config.serverUrl ?? DEFAULT_SERVER_URL;
245
+ const mcpMode = config.repoContext?.mcpMode;
246
+ return pipe2(
247
+ createMcpClient(serverUrl, config.token, mcpMode),
248
+ Effect2.flatMap(
249
+ (client) => pipe2(
250
+ createMcpServer(client, config.repoContext),
251
+ Effect2.map((server) => {
252
+ const close = async () => {
253
+ await server.close();
254
+ await client.close();
255
+ };
256
+ return { server, client, close };
257
+ })
258
+ )
259
+ )
260
+ );
261
+ };
262
+ var buildRepoContext = (repoConfig) => {
263
+ if (!repoConfig) {
264
+ return void 0;
265
+ }
266
+ const context = {};
267
+ if (repoConfig.orgProjects !== void 0) {
268
+ context.orgProjects = repoConfig.orgProjects;
269
+ }
270
+ if (repoConfig.personalProjects !== void 0) {
271
+ context.personalProjects = repoConfig.personalProjects;
272
+ }
273
+ if (repoConfig.profile !== void 0) {
274
+ context.profile = repoConfig.profile;
275
+ }
276
+ if (repoConfig.org !== void 0) {
277
+ context.org = repoConfig.org;
278
+ }
279
+ if (repoConfig.includeUserGlobalRules !== void 0) {
280
+ context.includeUserGlobalRules = repoConfig.includeUserGlobalRules;
281
+ }
282
+ if (repoConfig.includeOrgGlobalRules !== void 0) {
283
+ context.includeOrgGlobalRules = repoConfig.includeOrgGlobalRules;
284
+ }
285
+ if (repoConfig.mcpMode !== void 0) {
286
+ context.mcpMode = repoConfig.mcpMode;
287
+ }
288
+ return Object.keys(context).length > 0 ? context : void 0;
289
+ };
290
+ var logConfigPaths = (repoConfig) => {
291
+ if (repoConfig?.configPath) {
292
+ process2.stderr.write(`Loaded config from ${repoConfig.configPath}
293
+ `);
294
+ }
295
+ if (repoConfig?.userConfigPath) {
296
+ process2.stderr.write(
297
+ `Loaded user config from ${repoConfig.userConfigPath}
298
+ `
299
+ );
300
+ }
301
+ };
302
+ var resolveConfig = () => pipe2(
303
+ loadRepoConfig(),
304
+ Effect2.flatMap((repoConfig) => {
305
+ const token = repoConfig?.token ?? process2.env.BRAID_TOKEN;
306
+ if (!token) {
307
+ return Effect2.fail(
308
+ new ProxyError({
309
+ code: "missing_token",
310
+ message: "BRAID_TOKEN environment variable is required"
311
+ })
312
+ );
313
+ }
314
+ const serverUrl = repoConfig?.serverUrl ?? process2.env.BRAID_MCP_URL;
315
+ logConfigPaths(repoConfig);
316
+ return Effect2.succeed({
317
+ token,
318
+ serverUrl,
319
+ repoContext: buildRepoContext(repoConfig)
320
+ });
321
+ })
322
+ );
323
+ var startProxy = () => pipe2(
324
+ resolveConfig(),
325
+ Effect2.flatMap(createProxyServer),
326
+ Effect2.flatMap(
327
+ ({ close }) => Effect2.async(() => {
328
+ const shutdown = async () => {
329
+ await close();
330
+ process2.exit(0);
331
+ };
332
+ process2.on("SIGINT", shutdown);
333
+ process2.on("SIGTERM", shutdown);
334
+ process2.stdin.resume();
335
+ })
336
+ )
337
+ );
338
+ var startProxyFromEnv = async () => {
339
+ await Effect2.runPromise(
340
+ pipe2(
341
+ startProxy(),
342
+ Effect2.catchAll((error) => {
343
+ process2.stderr.write(`Error starting proxy: ${error.message}
344
+ `);
345
+ return process2.exit(1);
346
+ })
347
+ )
348
+ );
349
+ };
350
+
351
+ // src/cli.ts
352
+ startProxyFromEnv().catch(() => void 0);
353
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/proxy.ts","../src/config.ts","../src/cli.ts"],"sourcesContent":["import process from \"node:process\";\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { Data, Effect, pipe } from \"effect\";\nimport type { RepoContextConfig, ResolvedRepoConfig } from \"./config.ts\";\nimport { hasContextOverrides, loadRepoConfig } from \"./config.ts\";\n\ninterface ProxyConfig {\n token: string;\n serverUrl?: string | undefined;\n repoContext?: RepoContextConfig | undefined;\n}\n\nclass ProxyError extends Data.TaggedError(\"ProxyError\")<{\n code: \"missing_token\" | \"connection_error\" | \"runtime_error\";\n message: string;\n}> {}\n\nconst DEFAULT_SERVER_URL = \"https://app.braid.cloud/api/mcp\";\n\nconst createMcpClient = (\n serverUrl: string,\n token: string,\n mcpMode?: string\n): Effect.Effect<Client, ProxyError> =>\n Effect.tryPromise({\n try: async () => {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${token}`,\n };\n\n if (mcpMode) {\n headers[\"X-Braid-Mcp-Mode\"] = mcpMode;\n }\n\n const clientTransport = new StreamableHTTPClientTransport(\n new URL(serverUrl),\n {\n requestInit: {\n headers,\n },\n }\n );\n\n const client = new Client(\n { name: \"braid-mcp-proxy\", version: \"0.1.0\" },\n { capabilities: {} }\n );\n\n await client.connect(clientTransport as Transport);\n return client;\n },\n catch: (e) =>\n new ProxyError({\n code: \"connection_error\",\n message: e instanceof Error ? e.message : \"Failed to connect to server\",\n }),\n });\n\nconst injectContext = (\n args: Record<string, unknown>,\n repoContext: RepoContextConfig | undefined\n): Record<string, unknown> => {\n if (!(repoContext && hasContextOverrides(repoContext))) {\n return args;\n }\n\n const result = { ...args };\n\n if (repoContext.orgProjects !== undefined && args.orgProjects === undefined) {\n result.orgProjects = repoContext.orgProjects;\n }\n if (\n repoContext.personalProjects !== undefined &&\n args.personalProjects === undefined\n ) {\n result.personalProjects = repoContext.personalProjects;\n }\n if (repoContext.profile !== undefined && args.profile === undefined) {\n result.profile = repoContext.profile;\n }\n if (repoContext.org !== undefined && args.org === undefined) {\n result.org = repoContext.org;\n }\n if (\n repoContext.includeUserGlobalRules !== undefined &&\n args.includeUserGlobalRules === undefined\n ) {\n result.includeUserGlobalRules = repoContext.includeUserGlobalRules;\n }\n if (\n repoContext.includeOrgGlobalRules !== undefined &&\n args.includeOrgGlobalRules === undefined\n ) {\n result.includeOrgGlobalRules = repoContext.includeOrgGlobalRules;\n }\n\n return result;\n};\n\nconst createMcpServer = (\n client: Client,\n repoContext: RepoContextConfig | undefined\n): Effect.Effect<Server, ProxyError> =>\n Effect.tryPromise({\n try: async () => {\n const serverTransport = new StdioServerTransport();\n\n const server = new Server(\n { name: \"braid\", version: \"0.1.0\" },\n { capabilities: { tools: {} } }\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n const result = await client.listTools();\n return result;\n });\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const injectedArgs = injectContext(\n request.params.arguments ?? {},\n repoContext\n );\n const result = await client.callTool({\n name: request.params.name,\n arguments: injectedArgs,\n });\n return result;\n });\n\n await server.connect(serverTransport);\n return server;\n },\n catch: (e) =>\n new ProxyError({\n code: \"runtime_error\",\n message: e instanceof Error ? e.message : \"Failed to create server\",\n }),\n });\n\nconst createProxyServer = (\n config: ProxyConfig\n): Effect.Effect<\n { server: Server; client: Client; close: () => Promise<void> },\n ProxyError\n> => {\n const serverUrl = config.serverUrl ?? DEFAULT_SERVER_URL;\n const mcpMode = config.repoContext?.mcpMode;\n\n return pipe(\n createMcpClient(serverUrl, config.token, mcpMode),\n Effect.flatMap((client) =>\n pipe(\n createMcpServer(client, config.repoContext),\n Effect.map((server) => {\n const close = async () => {\n await server.close();\n await client.close();\n };\n return { server, client, close };\n })\n )\n )\n );\n};\n\nconst buildRepoContext = (\n repoConfig: ResolvedRepoConfig | null\n): RepoContextConfig | undefined => {\n if (!repoConfig) {\n return undefined;\n }\n\n const context: RepoContextConfig = {};\n\n if (repoConfig.orgProjects !== undefined) {\n context.orgProjects = repoConfig.orgProjects;\n }\n if (repoConfig.personalProjects !== undefined) {\n context.personalProjects = repoConfig.personalProjects;\n }\n if (repoConfig.profile !== undefined) {\n context.profile = repoConfig.profile;\n }\n if (repoConfig.org !== undefined) {\n context.org = repoConfig.org;\n }\n if (repoConfig.includeUserGlobalRules !== undefined) {\n context.includeUserGlobalRules = repoConfig.includeUserGlobalRules;\n }\n if (repoConfig.includeOrgGlobalRules !== undefined) {\n context.includeOrgGlobalRules = repoConfig.includeOrgGlobalRules;\n }\n if (repoConfig.mcpMode !== undefined) {\n context.mcpMode = repoConfig.mcpMode;\n }\n\n return Object.keys(context).length > 0 ? context : undefined;\n};\n\nconst logConfigPaths = (\n repoConfig: {\n configPath?: string;\n userConfigPath?: string;\n mcpMode?: string;\n } | null\n): void => {\n if (repoConfig?.configPath) {\n process.stderr.write(`Loaded config from ${repoConfig.configPath}\\n`);\n }\n if (repoConfig?.userConfigPath) {\n process.stderr.write(\n `Loaded user config from ${repoConfig.userConfigPath}\\n`\n );\n }\n};\n\nconst resolveConfig = (): Effect.Effect<ProxyConfig, ProxyError> =>\n pipe(\n loadRepoConfig(),\n Effect.flatMap((repoConfig) => {\n const token = repoConfig?.token ?? process.env.BRAID_TOKEN;\n if (!token) {\n return Effect.fail(\n new ProxyError({\n code: \"missing_token\",\n message: \"BRAID_TOKEN environment variable is required\",\n })\n );\n }\n\n const serverUrl = repoConfig?.serverUrl ?? process.env.BRAID_MCP_URL;\n logConfigPaths(repoConfig);\n\n return Effect.succeed({\n token,\n serverUrl,\n repoContext: buildRepoContext(repoConfig),\n });\n })\n );\n\nconst startProxy = (): Effect.Effect<void, ProxyError> =>\n pipe(\n resolveConfig(),\n Effect.flatMap(createProxyServer),\n Effect.flatMap(({ close }) =>\n Effect.async<void, ProxyError>(() => {\n const shutdown = async () => {\n await close();\n process.exit(0);\n };\n\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n process.stdin.resume();\n })\n )\n );\n\nconst startProxyFromEnv = async (): Promise<void> => {\n await Effect.runPromise(\n pipe(\n startProxy(),\n Effect.catchAll((error): Effect.Effect<never> => {\n process.stderr.write(`Error starting proxy: ${error.message}\\n`);\n return process.exit(1) as never;\n })\n )\n );\n};\n\nexport type { ProxyConfig };\nexport { createProxyServer, startProxyFromEnv };\n","import { readFile } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport process from \"node:process\";\nimport { Data, Effect, pipe } from \"effect\";\n\ntype McpMode = \"search\" | \"sync\";\n\ninterface RepoContextConfig {\n orgProjects?: string[];\n personalProjects?: string[];\n profile?: string;\n org?: string;\n includeUserGlobalRules?: boolean;\n includeOrgGlobalRules?: boolean;\n mcpMode?: McpMode;\n}\n\ninterface RepoConfig {\n $schema?: string;\n orgProjects?: string[];\n personalProjects?: string[];\n profile?: string;\n org?: string;\n token?: string;\n serverUrl?: string;\n includeUserGlobalRules?: boolean;\n includeOrgGlobalRules?: boolean;\n mcpMode?: McpMode;\n}\n\ninterface ResolvedRepoConfig {\n orgProjects?: string[];\n personalProjects?: string[];\n profile?: string;\n org?: string;\n token?: string;\n serverUrl?: string;\n includeUserGlobalRules?: boolean;\n includeOrgGlobalRules?: boolean;\n mcpMode?: McpMode;\n configPath?: string;\n userConfigPath?: string;\n}\n\nconst CONFIG_FILENAME = \"braid.json\";\nconst USER_CONFIG_FILENAME = \"braid.user.json\";\n\nclass FileReadError extends Data.TaggedError(\"FileReadError\")<\n Record<string, never>\n> {}\n\nconst readJsonFile = <T>(path: string): Effect.Effect<T | null> =>\n pipe(\n Effect.tryPromise({\n try: () => readFile(path, \"utf-8\"),\n catch: () => new FileReadError({}),\n }),\n Effect.flatMap((content) =>\n pipe(\n Effect.try(() => JSON.parse(content) as T),\n Effect.orElseSucceed(() => null)\n )\n ),\n Effect.orElseSucceed(() => null)\n );\n\nconst fileExists = (path: string): Effect.Effect<boolean> =>\n pipe(\n Effect.tryPromise({\n try: () => readFile(path),\n catch: () => new FileReadError({}),\n }),\n Effect.map(() => true),\n Effect.orElseSucceed(() => false)\n );\n\nconst findConfigDir = (startDir: string): Effect.Effect<string | null> => {\n const search = (currentDir: string): Effect.Effect<string | null> => {\n const root = dirname(currentDir);\n if (currentDir === root) {\n return Effect.succeed(null);\n }\n\n const configPath = join(currentDir, CONFIG_FILENAME);\n const userConfigPath = join(currentDir, USER_CONFIG_FILENAME);\n\n return pipe(\n Effect.all([fileExists(configPath), fileExists(userConfigPath)]),\n Effect.flatMap(([hasConfig, hasUserConfig]) =>\n hasConfig || hasUserConfig\n ? Effect.succeed(currentDir)\n : search(dirname(currentDir))\n )\n );\n };\n\n return search(resolve(startDir));\n};\n\ninterface MergedConfigValues {\n orgProjects: string[] | undefined;\n personalProjects: string[] | undefined;\n profile: string | undefined;\n org: string | undefined;\n token: string | undefined;\n serverUrl: string | undefined;\n includeUserGlobalRules: boolean | undefined;\n includeOrgGlobalRules: boolean | undefined;\n mcpMode: McpMode | undefined;\n}\n\nconst mergeConfigValues = (\n baseConfig: RepoConfig | null,\n userConfig: RepoConfig | null\n): MergedConfigValues => {\n return {\n orgProjects: userConfig?.orgProjects ?? baseConfig?.orgProjects,\n personalProjects:\n userConfig?.personalProjects ?? baseConfig?.personalProjects,\n profile: userConfig?.profile ?? baseConfig?.profile,\n org: userConfig?.org ?? baseConfig?.org,\n token: userConfig?.token ?? baseConfig?.token,\n serverUrl: userConfig?.serverUrl ?? baseConfig?.serverUrl,\n includeUserGlobalRules:\n userConfig?.includeUserGlobalRules ?? baseConfig?.includeUserGlobalRules,\n includeOrgGlobalRules:\n userConfig?.includeOrgGlobalRules ?? baseConfig?.includeOrgGlobalRules,\n mcpMode: userConfig?.mcpMode ?? baseConfig?.mcpMode,\n };\n};\n\nconst filterDefinedValues = (\n merged: MergedConfigValues,\n baseConfig: RepoConfig | null,\n userConfig: RepoConfig | null,\n configPath: string,\n userConfigPath: string\n): ResolvedRepoConfig => {\n const result: ResolvedRepoConfig = {};\n\n if (merged.orgProjects !== undefined) {\n result.orgProjects = merged.orgProjects;\n }\n if (merged.personalProjects !== undefined) {\n result.personalProjects = merged.personalProjects;\n }\n if (merged.profile !== undefined) {\n result.profile = merged.profile;\n }\n if (merged.org !== undefined) {\n result.org = merged.org;\n }\n if (merged.token !== undefined) {\n result.token = merged.token;\n }\n if (merged.serverUrl !== undefined) {\n result.serverUrl = merged.serverUrl;\n }\n if (merged.includeUserGlobalRules !== undefined) {\n result.includeUserGlobalRules = merged.includeUserGlobalRules;\n }\n if (merged.includeOrgGlobalRules !== undefined) {\n result.includeOrgGlobalRules = merged.includeOrgGlobalRules;\n }\n if (merged.mcpMode !== undefined) {\n result.mcpMode = merged.mcpMode;\n }\n if (baseConfig) {\n result.configPath = configPath;\n }\n if (userConfig) {\n result.userConfigPath = userConfigPath;\n }\n\n return result;\n};\n\nconst buildResolvedConfig = (\n baseConfig: RepoConfig | null,\n userConfig: RepoConfig | null,\n configPath: string,\n userConfigPath: string\n): ResolvedRepoConfig | null => {\n if (!(baseConfig || userConfig)) {\n return null;\n }\n\n const merged = mergeConfigValues(baseConfig, userConfig);\n return filterDefinedValues(\n merged,\n baseConfig,\n userConfig,\n configPath,\n userConfigPath\n );\n};\n\nconst loadRepoConfig = (\n startDir?: string\n): Effect.Effect<ResolvedRepoConfig | null> => {\n const searchDir = startDir ?? process.env.BRAID_CONFIG_DIR ?? process.cwd();\n\n return pipe(\n findConfigDir(searchDir),\n Effect.flatMap((configDir) => {\n if (!configDir) {\n return Effect.succeed(null);\n }\n\n const configPath = join(configDir, CONFIG_FILENAME);\n const userConfigPath = join(configDir, USER_CONFIG_FILENAME);\n\n return pipe(\n Effect.all({\n baseConfig: readJsonFile<RepoConfig>(configPath),\n userConfig: readJsonFile<RepoConfig>(userConfigPath),\n }),\n Effect.map(({ baseConfig, userConfig }) =>\n buildResolvedConfig(\n baseConfig,\n userConfig,\n configPath,\n userConfigPath\n )\n )\n );\n })\n );\n};\n\nconst loadRepoConfigAsync = (\n startDir?: string\n): Promise<ResolvedRepoConfig | null> =>\n Effect.runPromise(loadRepoConfig(startDir));\n\nconst hasContextOverrides = (config: RepoContextConfig): boolean =>\n Boolean(\n config.orgProjects?.length ||\n config.personalProjects?.length ||\n config.profile ||\n config.org\n );\n\nexport type { McpMode, RepoConfig, RepoContextConfig, ResolvedRepoConfig };\nexport { hasContextOverrides, loadRepoConfig, loadRepoConfigAsync };\n","import { startProxyFromEnv } from \"./proxy.ts\";\n\nstartProxyFromEnv().catch(() => undefined);\n"],"mappings":"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","names":["process","Data","Effect","pipe","Data","Effect","pipe","process"]}
@@ -0,0 +1,38 @@
1
+ import * as effect_Cause from 'effect/Cause';
2
+ import * as effect_Types from 'effect/Types';
3
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
4
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
5
+ import { Effect } from 'effect';
6
+
7
+ type McpMode = "search" | "sync";
8
+ interface RepoContextConfig {
9
+ orgProjects?: string[];
10
+ personalProjects?: string[];
11
+ profile?: string;
12
+ org?: string;
13
+ includeUserGlobalRules?: boolean;
14
+ includeOrgGlobalRules?: boolean;
15
+ mcpMode?: McpMode;
16
+ }
17
+
18
+ interface ProxyConfig {
19
+ token: string;
20
+ serverUrl?: string | undefined;
21
+ repoContext?: RepoContextConfig | undefined;
22
+ }
23
+ declare const ProxyError_base: new <A extends Record<string, any> = {}>(args: effect_Types.Equals<A, {}> extends true ? void : { readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }) => effect_Cause.YieldableError & {
24
+ readonly _tag: "ProxyError";
25
+ } & Readonly<A>;
26
+ declare class ProxyError extends ProxyError_base<{
27
+ code: "missing_token" | "connection_error" | "runtime_error";
28
+ message: string;
29
+ }> {
30
+ }
31
+ declare const createProxyServer: (config: ProxyConfig) => Effect.Effect<{
32
+ server: Server;
33
+ client: Client;
34
+ close: () => Promise<void>;
35
+ }, ProxyError>;
36
+ declare const startProxyFromEnv: () => Promise<void>;
37
+
38
+ export { type ProxyConfig, createProxyServer, startProxyFromEnv };
package/dist/proxy.js ADDED
@@ -0,0 +1,354 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/proxy.ts
4
+ import process2 from "process";
5
+ import { Client } from "@modelcontextprotocol/sdk/client/index.js";
6
+ import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
7
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
8
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
+ import {
10
+ CallToolRequestSchema,
11
+ ListToolsRequestSchema
12
+ } from "@modelcontextprotocol/sdk/types.js";
13
+ import { Data as Data2, Effect as Effect2, pipe as pipe2 } from "effect";
14
+
15
+ // src/config.ts
16
+ import { readFile } from "fs/promises";
17
+ import { dirname, join, resolve } from "path";
18
+ import process from "process";
19
+ import { Data, Effect, pipe } from "effect";
20
+ var CONFIG_FILENAME = "braid.json";
21
+ var USER_CONFIG_FILENAME = "braid.user.json";
22
+ var FileReadError = class extends Data.TaggedError("FileReadError") {
23
+ };
24
+ var readJsonFile = (path) => pipe(
25
+ Effect.tryPromise({
26
+ try: () => readFile(path, "utf-8"),
27
+ catch: () => new FileReadError({})
28
+ }),
29
+ Effect.flatMap(
30
+ (content) => pipe(
31
+ Effect.try(() => JSON.parse(content)),
32
+ Effect.orElseSucceed(() => null)
33
+ )
34
+ ),
35
+ Effect.orElseSucceed(() => null)
36
+ );
37
+ var fileExists = (path) => pipe(
38
+ Effect.tryPromise({
39
+ try: () => readFile(path),
40
+ catch: () => new FileReadError({})
41
+ }),
42
+ Effect.map(() => true),
43
+ Effect.orElseSucceed(() => false)
44
+ );
45
+ var findConfigDir = (startDir) => {
46
+ const search = (currentDir) => {
47
+ const root = dirname(currentDir);
48
+ if (currentDir === root) {
49
+ return Effect.succeed(null);
50
+ }
51
+ const configPath = join(currentDir, CONFIG_FILENAME);
52
+ const userConfigPath = join(currentDir, USER_CONFIG_FILENAME);
53
+ return pipe(
54
+ Effect.all([fileExists(configPath), fileExists(userConfigPath)]),
55
+ Effect.flatMap(
56
+ ([hasConfig, hasUserConfig]) => hasConfig || hasUserConfig ? Effect.succeed(currentDir) : search(dirname(currentDir))
57
+ )
58
+ );
59
+ };
60
+ return search(resolve(startDir));
61
+ };
62
+ var mergeConfigValues = (baseConfig, userConfig) => {
63
+ return {
64
+ orgProjects: userConfig?.orgProjects ?? baseConfig?.orgProjects,
65
+ personalProjects: userConfig?.personalProjects ?? baseConfig?.personalProjects,
66
+ profile: userConfig?.profile ?? baseConfig?.profile,
67
+ org: userConfig?.org ?? baseConfig?.org,
68
+ token: userConfig?.token ?? baseConfig?.token,
69
+ serverUrl: userConfig?.serverUrl ?? baseConfig?.serverUrl,
70
+ includeUserGlobalRules: userConfig?.includeUserGlobalRules ?? baseConfig?.includeUserGlobalRules,
71
+ includeOrgGlobalRules: userConfig?.includeOrgGlobalRules ?? baseConfig?.includeOrgGlobalRules,
72
+ mcpMode: userConfig?.mcpMode ?? baseConfig?.mcpMode
73
+ };
74
+ };
75
+ var filterDefinedValues = (merged, baseConfig, userConfig, configPath, userConfigPath) => {
76
+ const result = {};
77
+ if (merged.orgProjects !== void 0) {
78
+ result.orgProjects = merged.orgProjects;
79
+ }
80
+ if (merged.personalProjects !== void 0) {
81
+ result.personalProjects = merged.personalProjects;
82
+ }
83
+ if (merged.profile !== void 0) {
84
+ result.profile = merged.profile;
85
+ }
86
+ if (merged.org !== void 0) {
87
+ result.org = merged.org;
88
+ }
89
+ if (merged.token !== void 0) {
90
+ result.token = merged.token;
91
+ }
92
+ if (merged.serverUrl !== void 0) {
93
+ result.serverUrl = merged.serverUrl;
94
+ }
95
+ if (merged.includeUserGlobalRules !== void 0) {
96
+ result.includeUserGlobalRules = merged.includeUserGlobalRules;
97
+ }
98
+ if (merged.includeOrgGlobalRules !== void 0) {
99
+ result.includeOrgGlobalRules = merged.includeOrgGlobalRules;
100
+ }
101
+ if (merged.mcpMode !== void 0) {
102
+ result.mcpMode = merged.mcpMode;
103
+ }
104
+ if (baseConfig) {
105
+ result.configPath = configPath;
106
+ }
107
+ if (userConfig) {
108
+ result.userConfigPath = userConfigPath;
109
+ }
110
+ return result;
111
+ };
112
+ var buildResolvedConfig = (baseConfig, userConfig, configPath, userConfigPath) => {
113
+ if (!(baseConfig || userConfig)) {
114
+ return null;
115
+ }
116
+ const merged = mergeConfigValues(baseConfig, userConfig);
117
+ return filterDefinedValues(
118
+ merged,
119
+ baseConfig,
120
+ userConfig,
121
+ configPath,
122
+ userConfigPath
123
+ );
124
+ };
125
+ var loadRepoConfig = (startDir) => {
126
+ const searchDir = startDir ?? process.env.BRAID_CONFIG_DIR ?? process.cwd();
127
+ return pipe(
128
+ findConfigDir(searchDir),
129
+ Effect.flatMap((configDir) => {
130
+ if (!configDir) {
131
+ return Effect.succeed(null);
132
+ }
133
+ const configPath = join(configDir, CONFIG_FILENAME);
134
+ const userConfigPath = join(configDir, USER_CONFIG_FILENAME);
135
+ return pipe(
136
+ Effect.all({
137
+ baseConfig: readJsonFile(configPath),
138
+ userConfig: readJsonFile(userConfigPath)
139
+ }),
140
+ Effect.map(
141
+ ({ baseConfig, userConfig }) => buildResolvedConfig(
142
+ baseConfig,
143
+ userConfig,
144
+ configPath,
145
+ userConfigPath
146
+ )
147
+ )
148
+ );
149
+ })
150
+ );
151
+ };
152
+ var hasContextOverrides = (config) => Boolean(
153
+ config.orgProjects?.length || config.personalProjects?.length || config.profile || config.org
154
+ );
155
+
156
+ // src/proxy.ts
157
+ var ProxyError = class extends Data2.TaggedError("ProxyError") {
158
+ };
159
+ var DEFAULT_SERVER_URL = "https://app.braid.cloud/api/mcp";
160
+ var createMcpClient = (serverUrl, token, mcpMode) => Effect2.tryPromise({
161
+ try: async () => {
162
+ const headers = {
163
+ Authorization: `Bearer ${token}`
164
+ };
165
+ if (mcpMode) {
166
+ headers["X-Braid-Mcp-Mode"] = mcpMode;
167
+ }
168
+ const clientTransport = new StreamableHTTPClientTransport(
169
+ new URL(serverUrl),
170
+ {
171
+ requestInit: {
172
+ headers
173
+ }
174
+ }
175
+ );
176
+ const client = new Client(
177
+ { name: "braid-mcp-proxy", version: "0.1.0" },
178
+ { capabilities: {} }
179
+ );
180
+ await client.connect(clientTransport);
181
+ return client;
182
+ },
183
+ catch: (e) => new ProxyError({
184
+ code: "connection_error",
185
+ message: e instanceof Error ? e.message : "Failed to connect to server"
186
+ })
187
+ });
188
+ var injectContext = (args, repoContext) => {
189
+ if (!(repoContext && hasContextOverrides(repoContext))) {
190
+ return args;
191
+ }
192
+ const result = { ...args };
193
+ if (repoContext.orgProjects !== void 0 && args.orgProjects === void 0) {
194
+ result.orgProjects = repoContext.orgProjects;
195
+ }
196
+ if (repoContext.personalProjects !== void 0 && args.personalProjects === void 0) {
197
+ result.personalProjects = repoContext.personalProjects;
198
+ }
199
+ if (repoContext.profile !== void 0 && args.profile === void 0) {
200
+ result.profile = repoContext.profile;
201
+ }
202
+ if (repoContext.org !== void 0 && args.org === void 0) {
203
+ result.org = repoContext.org;
204
+ }
205
+ if (repoContext.includeUserGlobalRules !== void 0 && args.includeUserGlobalRules === void 0) {
206
+ result.includeUserGlobalRules = repoContext.includeUserGlobalRules;
207
+ }
208
+ if (repoContext.includeOrgGlobalRules !== void 0 && args.includeOrgGlobalRules === void 0) {
209
+ result.includeOrgGlobalRules = repoContext.includeOrgGlobalRules;
210
+ }
211
+ return result;
212
+ };
213
+ var createMcpServer = (client, repoContext) => Effect2.tryPromise({
214
+ try: async () => {
215
+ const serverTransport = new StdioServerTransport();
216
+ const server = new Server(
217
+ { name: "braid", version: "0.1.0" },
218
+ { capabilities: { tools: {} } }
219
+ );
220
+ server.setRequestHandler(ListToolsRequestSchema, async () => {
221
+ const result = await client.listTools();
222
+ return result;
223
+ });
224
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
225
+ const injectedArgs = injectContext(
226
+ request.params.arguments ?? {},
227
+ repoContext
228
+ );
229
+ const result = await client.callTool({
230
+ name: request.params.name,
231
+ arguments: injectedArgs
232
+ });
233
+ return result;
234
+ });
235
+ await server.connect(serverTransport);
236
+ return server;
237
+ },
238
+ catch: (e) => new ProxyError({
239
+ code: "runtime_error",
240
+ message: e instanceof Error ? e.message : "Failed to create server"
241
+ })
242
+ });
243
+ var createProxyServer = (config) => {
244
+ const serverUrl = config.serverUrl ?? DEFAULT_SERVER_URL;
245
+ const mcpMode = config.repoContext?.mcpMode;
246
+ return pipe2(
247
+ createMcpClient(serverUrl, config.token, mcpMode),
248
+ Effect2.flatMap(
249
+ (client) => pipe2(
250
+ createMcpServer(client, config.repoContext),
251
+ Effect2.map((server) => {
252
+ const close = async () => {
253
+ await server.close();
254
+ await client.close();
255
+ };
256
+ return { server, client, close };
257
+ })
258
+ )
259
+ )
260
+ );
261
+ };
262
+ var buildRepoContext = (repoConfig) => {
263
+ if (!repoConfig) {
264
+ return void 0;
265
+ }
266
+ const context = {};
267
+ if (repoConfig.orgProjects !== void 0) {
268
+ context.orgProjects = repoConfig.orgProjects;
269
+ }
270
+ if (repoConfig.personalProjects !== void 0) {
271
+ context.personalProjects = repoConfig.personalProjects;
272
+ }
273
+ if (repoConfig.profile !== void 0) {
274
+ context.profile = repoConfig.profile;
275
+ }
276
+ if (repoConfig.org !== void 0) {
277
+ context.org = repoConfig.org;
278
+ }
279
+ if (repoConfig.includeUserGlobalRules !== void 0) {
280
+ context.includeUserGlobalRules = repoConfig.includeUserGlobalRules;
281
+ }
282
+ if (repoConfig.includeOrgGlobalRules !== void 0) {
283
+ context.includeOrgGlobalRules = repoConfig.includeOrgGlobalRules;
284
+ }
285
+ if (repoConfig.mcpMode !== void 0) {
286
+ context.mcpMode = repoConfig.mcpMode;
287
+ }
288
+ return Object.keys(context).length > 0 ? context : void 0;
289
+ };
290
+ var logConfigPaths = (repoConfig) => {
291
+ if (repoConfig?.configPath) {
292
+ process2.stderr.write(`Loaded config from ${repoConfig.configPath}
293
+ `);
294
+ }
295
+ if (repoConfig?.userConfigPath) {
296
+ process2.stderr.write(
297
+ `Loaded user config from ${repoConfig.userConfigPath}
298
+ `
299
+ );
300
+ }
301
+ };
302
+ var resolveConfig = () => pipe2(
303
+ loadRepoConfig(),
304
+ Effect2.flatMap((repoConfig) => {
305
+ const token = repoConfig?.token ?? process2.env.BRAID_TOKEN;
306
+ if (!token) {
307
+ return Effect2.fail(
308
+ new ProxyError({
309
+ code: "missing_token",
310
+ message: "BRAID_TOKEN environment variable is required"
311
+ })
312
+ );
313
+ }
314
+ const serverUrl = repoConfig?.serverUrl ?? process2.env.BRAID_MCP_URL;
315
+ logConfigPaths(repoConfig);
316
+ return Effect2.succeed({
317
+ token,
318
+ serverUrl,
319
+ repoContext: buildRepoContext(repoConfig)
320
+ });
321
+ })
322
+ );
323
+ var startProxy = () => pipe2(
324
+ resolveConfig(),
325
+ Effect2.flatMap(createProxyServer),
326
+ Effect2.flatMap(
327
+ ({ close }) => Effect2.async(() => {
328
+ const shutdown = async () => {
329
+ await close();
330
+ process2.exit(0);
331
+ };
332
+ process2.on("SIGINT", shutdown);
333
+ process2.on("SIGTERM", shutdown);
334
+ process2.stdin.resume();
335
+ })
336
+ )
337
+ );
338
+ var startProxyFromEnv = async () => {
339
+ await Effect2.runPromise(
340
+ pipe2(
341
+ startProxy(),
342
+ Effect2.catchAll((error) => {
343
+ process2.stderr.write(`Error starting proxy: ${error.message}
344
+ `);
345
+ return process2.exit(1);
346
+ })
347
+ )
348
+ );
349
+ };
350
+ export {
351
+ createProxyServer,
352
+ startProxyFromEnv
353
+ };
354
+ //# sourceMappingURL=proxy.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/proxy.ts","../src/config.ts"],"sourcesContent":["import process from \"node:process\";\nimport { Client } from \"@modelcontextprotocol/sdk/client/index.js\";\nimport { StreamableHTTPClientTransport } from \"@modelcontextprotocol/sdk/client/streamableHttp.js\";\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport type { Transport } from \"@modelcontextprotocol/sdk/shared/transport.js\";\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from \"@modelcontextprotocol/sdk/types.js\";\nimport { Data, Effect, pipe } from \"effect\";\nimport type { RepoContextConfig, ResolvedRepoConfig } from \"./config.ts\";\nimport { hasContextOverrides, loadRepoConfig } from \"./config.ts\";\n\ninterface ProxyConfig {\n token: string;\n serverUrl?: string | undefined;\n repoContext?: RepoContextConfig | undefined;\n}\n\nclass ProxyError extends Data.TaggedError(\"ProxyError\")<{\n code: \"missing_token\" | \"connection_error\" | \"runtime_error\";\n message: string;\n}> {}\n\nconst DEFAULT_SERVER_URL = \"https://app.braid.cloud/api/mcp\";\n\nconst createMcpClient = (\n serverUrl: string,\n token: string,\n mcpMode?: string\n): Effect.Effect<Client, ProxyError> =>\n Effect.tryPromise({\n try: async () => {\n const headers: Record<string, string> = {\n Authorization: `Bearer ${token}`,\n };\n\n if (mcpMode) {\n headers[\"X-Braid-Mcp-Mode\"] = mcpMode;\n }\n\n const clientTransport = new StreamableHTTPClientTransport(\n new URL(serverUrl),\n {\n requestInit: {\n headers,\n },\n }\n );\n\n const client = new Client(\n { name: \"braid-mcp-proxy\", version: \"0.1.0\" },\n { capabilities: {} }\n );\n\n await client.connect(clientTransport as Transport);\n return client;\n },\n catch: (e) =>\n new ProxyError({\n code: \"connection_error\",\n message: e instanceof Error ? e.message : \"Failed to connect to server\",\n }),\n });\n\nconst injectContext = (\n args: Record<string, unknown>,\n repoContext: RepoContextConfig | undefined\n): Record<string, unknown> => {\n if (!(repoContext && hasContextOverrides(repoContext))) {\n return args;\n }\n\n const result = { ...args };\n\n if (repoContext.orgProjects !== undefined && args.orgProjects === undefined) {\n result.orgProjects = repoContext.orgProjects;\n }\n if (\n repoContext.personalProjects !== undefined &&\n args.personalProjects === undefined\n ) {\n result.personalProjects = repoContext.personalProjects;\n }\n if (repoContext.profile !== undefined && args.profile === undefined) {\n result.profile = repoContext.profile;\n }\n if (repoContext.org !== undefined && args.org === undefined) {\n result.org = repoContext.org;\n }\n if (\n repoContext.includeUserGlobalRules !== undefined &&\n args.includeUserGlobalRules === undefined\n ) {\n result.includeUserGlobalRules = repoContext.includeUserGlobalRules;\n }\n if (\n repoContext.includeOrgGlobalRules !== undefined &&\n args.includeOrgGlobalRules === undefined\n ) {\n result.includeOrgGlobalRules = repoContext.includeOrgGlobalRules;\n }\n\n return result;\n};\n\nconst createMcpServer = (\n client: Client,\n repoContext: RepoContextConfig | undefined\n): Effect.Effect<Server, ProxyError> =>\n Effect.tryPromise({\n try: async () => {\n const serverTransport = new StdioServerTransport();\n\n const server = new Server(\n { name: \"braid\", version: \"0.1.0\" },\n { capabilities: { tools: {} } }\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n const result = await client.listTools();\n return result;\n });\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const injectedArgs = injectContext(\n request.params.arguments ?? {},\n repoContext\n );\n const result = await client.callTool({\n name: request.params.name,\n arguments: injectedArgs,\n });\n return result;\n });\n\n await server.connect(serverTransport);\n return server;\n },\n catch: (e) =>\n new ProxyError({\n code: \"runtime_error\",\n message: e instanceof Error ? e.message : \"Failed to create server\",\n }),\n });\n\nconst createProxyServer = (\n config: ProxyConfig\n): Effect.Effect<\n { server: Server; client: Client; close: () => Promise<void> },\n ProxyError\n> => {\n const serverUrl = config.serverUrl ?? DEFAULT_SERVER_URL;\n const mcpMode = config.repoContext?.mcpMode;\n\n return pipe(\n createMcpClient(serverUrl, config.token, mcpMode),\n Effect.flatMap((client) =>\n pipe(\n createMcpServer(client, config.repoContext),\n Effect.map((server) => {\n const close = async () => {\n await server.close();\n await client.close();\n };\n return { server, client, close };\n })\n )\n )\n );\n};\n\nconst buildRepoContext = (\n repoConfig: ResolvedRepoConfig | null\n): RepoContextConfig | undefined => {\n if (!repoConfig) {\n return undefined;\n }\n\n const context: RepoContextConfig = {};\n\n if (repoConfig.orgProjects !== undefined) {\n context.orgProjects = repoConfig.orgProjects;\n }\n if (repoConfig.personalProjects !== undefined) {\n context.personalProjects = repoConfig.personalProjects;\n }\n if (repoConfig.profile !== undefined) {\n context.profile = repoConfig.profile;\n }\n if (repoConfig.org !== undefined) {\n context.org = repoConfig.org;\n }\n if (repoConfig.includeUserGlobalRules !== undefined) {\n context.includeUserGlobalRules = repoConfig.includeUserGlobalRules;\n }\n if (repoConfig.includeOrgGlobalRules !== undefined) {\n context.includeOrgGlobalRules = repoConfig.includeOrgGlobalRules;\n }\n if (repoConfig.mcpMode !== undefined) {\n context.mcpMode = repoConfig.mcpMode;\n }\n\n return Object.keys(context).length > 0 ? context : undefined;\n};\n\nconst logConfigPaths = (\n repoConfig: {\n configPath?: string;\n userConfigPath?: string;\n mcpMode?: string;\n } | null\n): void => {\n if (repoConfig?.configPath) {\n process.stderr.write(`Loaded config from ${repoConfig.configPath}\\n`);\n }\n if (repoConfig?.userConfigPath) {\n process.stderr.write(\n `Loaded user config from ${repoConfig.userConfigPath}\\n`\n );\n }\n};\n\nconst resolveConfig = (): Effect.Effect<ProxyConfig, ProxyError> =>\n pipe(\n loadRepoConfig(),\n Effect.flatMap((repoConfig) => {\n const token = repoConfig?.token ?? process.env.BRAID_TOKEN;\n if (!token) {\n return Effect.fail(\n new ProxyError({\n code: \"missing_token\",\n message: \"BRAID_TOKEN environment variable is required\",\n })\n );\n }\n\n const serverUrl = repoConfig?.serverUrl ?? process.env.BRAID_MCP_URL;\n logConfigPaths(repoConfig);\n\n return Effect.succeed({\n token,\n serverUrl,\n repoContext: buildRepoContext(repoConfig),\n });\n })\n );\n\nconst startProxy = (): Effect.Effect<void, ProxyError> =>\n pipe(\n resolveConfig(),\n Effect.flatMap(createProxyServer),\n Effect.flatMap(({ close }) =>\n Effect.async<void, ProxyError>(() => {\n const shutdown = async () => {\n await close();\n process.exit(0);\n };\n\n process.on(\"SIGINT\", shutdown);\n process.on(\"SIGTERM\", shutdown);\n process.stdin.resume();\n })\n )\n );\n\nconst startProxyFromEnv = async (): Promise<void> => {\n await Effect.runPromise(\n pipe(\n startProxy(),\n Effect.catchAll((error): Effect.Effect<never> => {\n process.stderr.write(`Error starting proxy: ${error.message}\\n`);\n return process.exit(1) as never;\n })\n )\n );\n};\n\nexport type { ProxyConfig };\nexport { createProxyServer, startProxyFromEnv };\n","import { readFile } from \"node:fs/promises\";\nimport { dirname, join, resolve } from \"node:path\";\nimport process from \"node:process\";\nimport { Data, Effect, pipe } from \"effect\";\n\ntype McpMode = \"search\" | \"sync\";\n\ninterface RepoContextConfig {\n orgProjects?: string[];\n personalProjects?: string[];\n profile?: string;\n org?: string;\n includeUserGlobalRules?: boolean;\n includeOrgGlobalRules?: boolean;\n mcpMode?: McpMode;\n}\n\ninterface RepoConfig {\n $schema?: string;\n orgProjects?: string[];\n personalProjects?: string[];\n profile?: string;\n org?: string;\n token?: string;\n serverUrl?: string;\n includeUserGlobalRules?: boolean;\n includeOrgGlobalRules?: boolean;\n mcpMode?: McpMode;\n}\n\ninterface ResolvedRepoConfig {\n orgProjects?: string[];\n personalProjects?: string[];\n profile?: string;\n org?: string;\n token?: string;\n serverUrl?: string;\n includeUserGlobalRules?: boolean;\n includeOrgGlobalRules?: boolean;\n mcpMode?: McpMode;\n configPath?: string;\n userConfigPath?: string;\n}\n\nconst CONFIG_FILENAME = \"braid.json\";\nconst USER_CONFIG_FILENAME = \"braid.user.json\";\n\nclass FileReadError extends Data.TaggedError(\"FileReadError\")<\n Record<string, never>\n> {}\n\nconst readJsonFile = <T>(path: string): Effect.Effect<T | null> =>\n pipe(\n Effect.tryPromise({\n try: () => readFile(path, \"utf-8\"),\n catch: () => new FileReadError({}),\n }),\n Effect.flatMap((content) =>\n pipe(\n Effect.try(() => JSON.parse(content) as T),\n Effect.orElseSucceed(() => null)\n )\n ),\n Effect.orElseSucceed(() => null)\n );\n\nconst fileExists = (path: string): Effect.Effect<boolean> =>\n pipe(\n Effect.tryPromise({\n try: () => readFile(path),\n catch: () => new FileReadError({}),\n }),\n Effect.map(() => true),\n Effect.orElseSucceed(() => false)\n );\n\nconst findConfigDir = (startDir: string): Effect.Effect<string | null> => {\n const search = (currentDir: string): Effect.Effect<string | null> => {\n const root = dirname(currentDir);\n if (currentDir === root) {\n return Effect.succeed(null);\n }\n\n const configPath = join(currentDir, CONFIG_FILENAME);\n const userConfigPath = join(currentDir, USER_CONFIG_FILENAME);\n\n return pipe(\n Effect.all([fileExists(configPath), fileExists(userConfigPath)]),\n Effect.flatMap(([hasConfig, hasUserConfig]) =>\n hasConfig || hasUserConfig\n ? Effect.succeed(currentDir)\n : search(dirname(currentDir))\n )\n );\n };\n\n return search(resolve(startDir));\n};\n\ninterface MergedConfigValues {\n orgProjects: string[] | undefined;\n personalProjects: string[] | undefined;\n profile: string | undefined;\n org: string | undefined;\n token: string | undefined;\n serverUrl: string | undefined;\n includeUserGlobalRules: boolean | undefined;\n includeOrgGlobalRules: boolean | undefined;\n mcpMode: McpMode | undefined;\n}\n\nconst mergeConfigValues = (\n baseConfig: RepoConfig | null,\n userConfig: RepoConfig | null\n): MergedConfigValues => {\n return {\n orgProjects: userConfig?.orgProjects ?? baseConfig?.orgProjects,\n personalProjects:\n userConfig?.personalProjects ?? baseConfig?.personalProjects,\n profile: userConfig?.profile ?? baseConfig?.profile,\n org: userConfig?.org ?? baseConfig?.org,\n token: userConfig?.token ?? baseConfig?.token,\n serverUrl: userConfig?.serverUrl ?? baseConfig?.serverUrl,\n includeUserGlobalRules:\n userConfig?.includeUserGlobalRules ?? baseConfig?.includeUserGlobalRules,\n includeOrgGlobalRules:\n userConfig?.includeOrgGlobalRules ?? baseConfig?.includeOrgGlobalRules,\n mcpMode: userConfig?.mcpMode ?? baseConfig?.mcpMode,\n };\n};\n\nconst filterDefinedValues = (\n merged: MergedConfigValues,\n baseConfig: RepoConfig | null,\n userConfig: RepoConfig | null,\n configPath: string,\n userConfigPath: string\n): ResolvedRepoConfig => {\n const result: ResolvedRepoConfig = {};\n\n if (merged.orgProjects !== undefined) {\n result.orgProjects = merged.orgProjects;\n }\n if (merged.personalProjects !== undefined) {\n result.personalProjects = merged.personalProjects;\n }\n if (merged.profile !== undefined) {\n result.profile = merged.profile;\n }\n if (merged.org !== undefined) {\n result.org = merged.org;\n }\n if (merged.token !== undefined) {\n result.token = merged.token;\n }\n if (merged.serverUrl !== undefined) {\n result.serverUrl = merged.serverUrl;\n }\n if (merged.includeUserGlobalRules !== undefined) {\n result.includeUserGlobalRules = merged.includeUserGlobalRules;\n }\n if (merged.includeOrgGlobalRules !== undefined) {\n result.includeOrgGlobalRules = merged.includeOrgGlobalRules;\n }\n if (merged.mcpMode !== undefined) {\n result.mcpMode = merged.mcpMode;\n }\n if (baseConfig) {\n result.configPath = configPath;\n }\n if (userConfig) {\n result.userConfigPath = userConfigPath;\n }\n\n return result;\n};\n\nconst buildResolvedConfig = (\n baseConfig: RepoConfig | null,\n userConfig: RepoConfig | null,\n configPath: string,\n userConfigPath: string\n): ResolvedRepoConfig | null => {\n if (!(baseConfig || userConfig)) {\n return null;\n }\n\n const merged = mergeConfigValues(baseConfig, userConfig);\n return filterDefinedValues(\n merged,\n baseConfig,\n userConfig,\n configPath,\n userConfigPath\n );\n};\n\nconst loadRepoConfig = (\n startDir?: string\n): Effect.Effect<ResolvedRepoConfig | null> => {\n const searchDir = startDir ?? process.env.BRAID_CONFIG_DIR ?? process.cwd();\n\n return pipe(\n findConfigDir(searchDir),\n Effect.flatMap((configDir) => {\n if (!configDir) {\n return Effect.succeed(null);\n }\n\n const configPath = join(configDir, CONFIG_FILENAME);\n const userConfigPath = join(configDir, USER_CONFIG_FILENAME);\n\n return pipe(\n Effect.all({\n baseConfig: readJsonFile<RepoConfig>(configPath),\n userConfig: readJsonFile<RepoConfig>(userConfigPath),\n }),\n Effect.map(({ baseConfig, userConfig }) =>\n buildResolvedConfig(\n baseConfig,\n userConfig,\n configPath,\n userConfigPath\n )\n )\n );\n })\n );\n};\n\nconst loadRepoConfigAsync = (\n startDir?: string\n): Promise<ResolvedRepoConfig | null> =>\n Effect.runPromise(loadRepoConfig(startDir));\n\nconst hasContextOverrides = (config: RepoContextConfig): boolean =>\n Boolean(\n config.orgProjects?.length ||\n config.personalProjects?.length ||\n config.profile ||\n config.org\n );\n\nexport type { McpMode, RepoConfig, RepoContextConfig, ResolvedRepoConfig };\nexport { hasContextOverrides, loadRepoConfig, loadRepoConfigAsync };\n"],"mappings":"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","names":["process","Data","Effect","pipe","Data","Effect","pipe","process"]}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@braid-cloud/mcp",
3
+ "version": "0.1.0",
4
+ "description": "braid MCP stdio proxy - forwards MCP requests to the hosted braid server",
5
+ "type": "module",
6
+ "bin": {
7
+ "braid-mcp": "./dist/cli.js"
8
+ },
9
+ "main": "./dist/proxy.js",
10
+ "types": "./dist/proxy.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/proxy.d.ts",
14
+ "import": "./dist/proxy.js"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "README.md"
20
+ ],
21
+ "scripts": {
22
+ "dev": "tsup --watch",
23
+ "dev:run": "tsx src/cli.ts",
24
+ "build": "tsup",
25
+ "lint": "biome check src",
26
+ "lint:fix": "biome check --write src",
27
+ "typecheck": "tsc --noEmit",
28
+ "prepublishOnly": "npm run build"
29
+ },
30
+ "dependencies": {
31
+ "@modelcontextprotocol/sdk": "^1.25.3",
32
+ "effect": "^3.19.14"
33
+ },
34
+ "devDependencies": {
35
+ "@braid-cloud/config": "workspace:*",
36
+ "@types/node": "^25.0.10",
37
+ "tsup": "^8.5.1",
38
+ "tsx": "^4.21.0",
39
+ "typescript": "^5.9.3"
40
+ },
41
+ "keywords": [
42
+ "mcp",
43
+ "model-context-protocol",
44
+ "braid",
45
+ "prompts",
46
+ "rules",
47
+ "ai"
48
+ ],
49
+ "author": "braid <hello@braid.cloud>",
50
+ "license": "MIT",
51
+ "homepage": "https://braid.cloud",
52
+ "engines": {
53
+ "node": ">=18"
54
+ }
55
+ }