@studiometa/forge-mcp 0.1.0 → 0.2.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.
- package/README.md +72 -22
- package/dist/flags-LFbdErsZ.js +23 -0
- package/dist/flags-LFbdErsZ.js.map +1 -0
- package/dist/flags.d.ts +19 -0
- package/dist/flags.d.ts.map +1 -0
- package/dist/formatters.d.ts +11 -3
- package/dist/formatters.d.ts.map +1 -1
- package/dist/handlers/index.d.ts +5 -3
- package/dist/handlers/index.d.ts.map +1 -1
- package/dist/handlers/types.d.ts +5 -0
- package/dist/handlers/types.d.ts.map +1 -1
- package/dist/handlers/utils.d.ts +6 -3
- package/dist/handlers/utils.d.ts.map +1 -1
- package/dist/{http-BJUKoZdb.js → http-w0DliUHY.js} +33 -9
- package/dist/http-w0DliUHY.js.map +1 -0
- package/dist/http.d.ts +11 -3
- package/dist/http.d.ts.map +1 -1
- package/dist/http.js +2 -2
- package/dist/index.d.ts +13 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +90 -32
- package/dist/index.js.map +1 -1
- package/dist/server.d.ts +10 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +15 -6
- package/dist/server.js.map +1 -1
- package/dist/stdio.d.ts +17 -2
- package/dist/stdio.d.ts.map +1 -1
- package/dist/tools.d.ts +36 -11
- package/dist/tools.d.ts.map +1 -1
- package/dist/{version-Cw8OGt4r.js → version-BmEJceWJ.js} +350 -130
- package/dist/version-BmEJceWJ.js.map +1 -0
- package/package.json +1 -1
- package/skills/SKILL.md +68 -38
- package/dist/http-BJUKoZdb.js.map +0 -1
- package/dist/version-Cw8OGt4r.js.map +0 -1
package/dist/index.js
CHANGED
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { t as parseReadOnlyFlag } from "./flags-LFbdErsZ.js";
|
|
3
|
+
import { a as INSTRUCTIONS, i as getTools, n as executeToolWithCredentials, r as STDIO_ONLY_TOOLS, t as VERSION } from "./version-BmEJceWJ.js";
|
|
3
4
|
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
|
|
4
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
6
|
import { CallToolRequestSchema, ListToolsRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
6
7
|
import { getToken, setToken } from "@studiometa/forge-api";
|
|
7
8
|
/**
|
|
8
9
|
* Get all available tools (including stdio-only configuration tools).
|
|
10
|
+
*
|
|
11
|
+
* @param options - Optional filtering. When `readOnly` is true, forge_write is excluded.
|
|
9
12
|
*/
|
|
10
|
-
function getAvailableTools() {
|
|
11
|
-
return [...
|
|
13
|
+
function getAvailableTools(options) {
|
|
14
|
+
return [...getTools(options), ...STDIO_ONLY_TOOLS];
|
|
12
15
|
}
|
|
13
16
|
/**
|
|
14
17
|
* Handle the forge_configure tool.
|
|
@@ -19,46 +22,91 @@ function handleConfigureTool(args) {
|
|
|
19
22
|
type: "text",
|
|
20
23
|
text: "Error: apiToken is required and must be a non-empty string."
|
|
21
24
|
}],
|
|
25
|
+
structuredContent: {
|
|
26
|
+
success: false,
|
|
27
|
+
error: "apiToken is required and must be a non-empty string."
|
|
28
|
+
},
|
|
22
29
|
isError: true
|
|
23
30
|
};
|
|
24
31
|
setToken(args.apiToken);
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
const data = {
|
|
33
|
+
success: true,
|
|
34
|
+
message: "Laravel Forge API token configured successfully",
|
|
35
|
+
apiToken: `***${args.apiToken.slice(-4)}`
|
|
36
|
+
};
|
|
37
|
+
return {
|
|
38
|
+
content: [{
|
|
39
|
+
type: "text",
|
|
40
|
+
text: JSON.stringify(data, null, 2)
|
|
41
|
+
}],
|
|
42
|
+
structuredContent: data
|
|
43
|
+
};
|
|
33
44
|
}
|
|
34
45
|
/**
|
|
35
46
|
* Handle the forge_get_config tool.
|
|
36
47
|
*/
|
|
37
48
|
function handleGetConfigTool() {
|
|
38
49
|
const token = getToken();
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
50
|
+
const data = {
|
|
51
|
+
apiToken: token ? `***${token.slice(-4)}` : "not configured",
|
|
52
|
+
configured: !!token
|
|
53
|
+
};
|
|
54
|
+
return {
|
|
55
|
+
content: [{
|
|
56
|
+
type: "text",
|
|
57
|
+
text: JSON.stringify(data, null, 2)
|
|
58
|
+
}],
|
|
59
|
+
structuredContent: data
|
|
60
|
+
};
|
|
46
61
|
}
|
|
47
62
|
/**
|
|
48
63
|
* Handle a tool call request.
|
|
64
|
+
*
|
|
65
|
+
* Routes to the appropriate handler based on tool name:
|
|
66
|
+
* - forge_configure / forge_get_config — stdio-only config tools
|
|
67
|
+
* - forge — read-only operations (list, get, help, schema)
|
|
68
|
+
* - forge_write — write operations (create, update, delete, deploy, etc.)
|
|
49
69
|
*/
|
|
50
|
-
async function handleToolCall(name, args) {
|
|
70
|
+
async function handleToolCall(name, args, options) {
|
|
51
71
|
if (name === "forge_configure") return handleConfigureTool(args);
|
|
52
72
|
if (name === "forge_get_config") return handleGetConfigTool();
|
|
53
|
-
|
|
54
|
-
|
|
73
|
+
if (name === "forge_write" && options?.readOnly) return {
|
|
74
|
+
content: [{
|
|
75
|
+
type: "text",
|
|
76
|
+
text: "Error: Server is running in read-only mode. Write operations are disabled."
|
|
77
|
+
}],
|
|
78
|
+
structuredContent: {
|
|
79
|
+
success: false,
|
|
80
|
+
error: "Server is running in read-only mode. Write operations are disabled."
|
|
81
|
+
},
|
|
82
|
+
isError: true
|
|
83
|
+
};
|
|
84
|
+
if (name === "forge" || name === "forge_write") {
|
|
85
|
+
const apiToken = getToken();
|
|
86
|
+
if (!apiToken) return {
|
|
87
|
+
content: [{
|
|
88
|
+
type: "text",
|
|
89
|
+
text: "Error: Forge API token not configured. Use \"forge_configure\" tool or set FORGE_API_TOKEN environment variable."
|
|
90
|
+
}],
|
|
91
|
+
structuredContent: {
|
|
92
|
+
success: false,
|
|
93
|
+
error: "Forge API token not configured. Use \"forge_configure\" tool or set FORGE_API_TOKEN environment variable."
|
|
94
|
+
},
|
|
95
|
+
isError: true
|
|
96
|
+
};
|
|
97
|
+
return executeToolWithCredentials(name, args, { apiToken });
|
|
98
|
+
}
|
|
99
|
+
return {
|
|
55
100
|
content: [{
|
|
56
101
|
type: "text",
|
|
57
|
-
text:
|
|
102
|
+
text: `Error: Unknown tool "${name}".`
|
|
58
103
|
}],
|
|
104
|
+
structuredContent: {
|
|
105
|
+
success: false,
|
|
106
|
+
error: `Unknown tool "${name}".`
|
|
107
|
+
},
|
|
59
108
|
isError: true
|
|
60
109
|
};
|
|
61
|
-
return executeToolWithCredentials(name, args, { apiToken });
|
|
62
110
|
}
|
|
63
111
|
/**
|
|
64
112
|
* Forge MCP Server — Stdio Transport
|
|
@@ -68,12 +116,15 @@ async function handleToolCall(name, args) {
|
|
|
68
116
|
*
|
|
69
117
|
* Usage:
|
|
70
118
|
* npx @studiometa/forge-mcp
|
|
119
|
+
* npx @studiometa/forge-mcp --read-only
|
|
120
|
+
* FORGE_READ_ONLY=true npx @studiometa/forge-mcp
|
|
71
121
|
*
|
|
72
122
|
* Or in Claude Desktop config:
|
|
73
123
|
* {
|
|
74
124
|
* "mcpServers": {
|
|
75
125
|
* "forge": {
|
|
76
126
|
* "command": "forge-mcp",
|
|
127
|
+
* "args": ["--read-only"],
|
|
77
128
|
* "env": { "FORGE_API_TOKEN": "your-token" }
|
|
78
129
|
* }
|
|
79
130
|
* }
|
|
@@ -82,7 +133,8 @@ async function handleToolCall(name, args) {
|
|
|
82
133
|
/**
|
|
83
134
|
* Create and configure the MCP server.
|
|
84
135
|
*/
|
|
85
|
-
function createStdioServer() {
|
|
136
|
+
function createStdioServer(options) {
|
|
137
|
+
const readOnly = options?.readOnly ?? false;
|
|
86
138
|
const server = new Server({
|
|
87
139
|
name: "forge-mcp",
|
|
88
140
|
version: VERSION
|
|
@@ -91,18 +143,23 @@ function createStdioServer() {
|
|
|
91
143
|
instructions: INSTRUCTIONS
|
|
92
144
|
});
|
|
93
145
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
94
|
-
return { tools: getAvailableTools() };
|
|
146
|
+
return { tools: getAvailableTools({ readOnly }) };
|
|
95
147
|
});
|
|
96
148
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
97
149
|
const { name, arguments: args } = request.params;
|
|
98
150
|
try {
|
|
99
|
-
return await handleToolCall(name, args ?? {});
|
|
151
|
+
return await handleToolCall(name, args ?? {}, { readOnly });
|
|
100
152
|
} catch (error) {
|
|
153
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
101
154
|
return {
|
|
102
155
|
content: [{
|
|
103
156
|
type: "text",
|
|
104
|
-
text: `Error: ${
|
|
157
|
+
text: `Error: ${message}`
|
|
105
158
|
}],
|
|
159
|
+
structuredContent: {
|
|
160
|
+
success: false,
|
|
161
|
+
error: message
|
|
162
|
+
},
|
|
106
163
|
isError: true
|
|
107
164
|
};
|
|
108
165
|
}
|
|
@@ -112,16 +169,17 @@ function createStdioServer() {
|
|
|
112
169
|
/**
|
|
113
170
|
* Start the stdio server.
|
|
114
171
|
*/
|
|
115
|
-
async function startStdioServer() {
|
|
116
|
-
const server = createStdioServer();
|
|
172
|
+
async function startStdioServer(options) {
|
|
173
|
+
const server = createStdioServer(options);
|
|
117
174
|
const transport = new StdioServerTransport();
|
|
118
175
|
await server.connect(transport);
|
|
119
|
-
|
|
176
|
+
const mode = options?.readOnly ? " (read-only)" : "";
|
|
177
|
+
console.error(`Forge MCP server v${VERSION} running on stdio${mode}`);
|
|
120
178
|
}
|
|
121
|
-
if (import.meta.url === `file://${process.argv[1]}` || process.argv[1]?.endsWith("/forge-mcp") || process.argv[1]?.endsWith("\\forge-mcp")) startStdioServer().catch((error) => {
|
|
179
|
+
if (import.meta.url === `file://${process.argv[1]}` || process.argv[1]?.endsWith("/forge-mcp") || process.argv[1]?.endsWith("\\forge-mcp")) startStdioServer({ readOnly: parseReadOnlyFlag() }).catch((error) => {
|
|
122
180
|
console.error("Fatal error:", error);
|
|
123
181
|
process.exit(1);
|
|
124
182
|
});
|
|
125
|
-
export { createStdioServer, startStdioServer };
|
|
183
|
+
export { createStdioServer, parseReadOnlyFlag, startStdioServer };
|
|
126
184
|
|
|
127
185
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/stdio.ts","../src/index.ts"],"sourcesContent":["import { getToken, setToken } from \"@studiometa/forge-api\";\n\nimport type { ToolResult } from \"./handlers/types.ts\";\n\nimport { executeToolWithCredentials } from \"./handlers/index.ts\";\nimport { STDIO_ONLY_TOOLS, TOOLS } from \"./tools.ts\";\n\nexport type { ToolResult };\n\n/**\n * Get all available tools (including stdio-only configuration tools).\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function getAvailableTools(): any[] {\n return [...TOOLS, ...STDIO_ONLY_TOOLS];\n}\n\n/**\n * Handle the forge_configure tool.\n */\nexport function handleConfigureTool(args: { apiToken: string }): ToolResult {\n if (!args.apiToken || typeof args.apiToken !== \"string\" || args.apiToken.trim().length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: \"Error: apiToken is required and must be a non-empty string.\",\n },\n ],\n isError: true,\n };\n }\n\n setToken(args.apiToken);\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n success: true,\n message: \"Laravel Forge API token configured successfully\",\n apiToken: `***${args.apiToken.slice(-4)}`,\n },\n null,\n 2,\n ),\n },\n ],\n };\n}\n\n/**\n * Handle the forge_get_config tool.\n */\nexport function handleGetConfigTool(): ToolResult {\n const token = getToken();\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(\n {\n apiToken: token ? `***${token.slice(-4)}` : \"not configured\",\n configured: !!token,\n },\n null,\n 2,\n ),\n },\n ],\n };\n}\n\n/**\n * Handle a tool call request.\n */\nexport async function handleToolCall(\n name: string,\n args: Record<string, unknown>,\n): Promise<ToolResult> {\n if (name === \"forge_configure\") {\n return handleConfigureTool(args as { apiToken: string });\n }\n\n if (name === \"forge_get_config\") {\n return handleGetConfigTool();\n }\n\n // Get API token\n const apiToken = getToken();\n if (!apiToken) {\n return {\n content: [\n {\n type: \"text\",\n text: 'Error: Forge API token not configured. Use \"forge_configure\" tool or set FORGE_API_TOKEN environment variable.',\n },\n ],\n isError: true,\n };\n }\n\n return executeToolWithCredentials(name, args, { apiToken });\n}\n","#!/usr/bin/env node\n\n/**\n * Forge MCP Server — Stdio Transport\n *\n * This is the local execution mode using stdio transport.\n * For remote HTTP deployment, use server.ts instead.\n *\n * Usage:\n * npx @studiometa/forge-mcp\n *\n * Or in Claude Desktop config:\n * {\n * \"mcpServers\": {\n * \"forge\": {\n * \"command\": \"forge-mcp\",\n * \"env\": { \"FORGE_API_TOKEN\": \"your-token\" }\n * }\n * }\n * }\n */\n\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { CallToolRequestSchema, ListToolsRequestSchema } from \"@modelcontextprotocol/sdk/types.js\";\n\nimport { INSTRUCTIONS } from \"./instructions.ts\";\nimport { getAvailableTools, handleToolCall } from \"./stdio.ts\";\nimport { VERSION } from \"./version.ts\";\n\n/**\n * Create and configure the MCP server.\n */\nexport function createStdioServer(): Server {\n const server = new Server(\n {\n name: \"forge-mcp\",\n version: VERSION,\n },\n {\n capabilities: {\n tools: {},\n },\n instructions: INSTRUCTIONS,\n },\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n return { tools: getAvailableTools() };\n });\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n try {\n const result = await handleToolCall(name, (args as Record<string, unknown>) ?? {});\n return result as unknown as Record<string, unknown>;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: \"text\" as const, text: `Error: ${message}` }],\n isError: true,\n };\n }\n });\n\n return server;\n}\n\n/**\n * Start the stdio server.\n */\nexport async function startStdioServer(): Promise<void> {\n const server = createStdioServer();\n const transport = new StdioServerTransport();\n await server.connect(transport);\n console.error(`Forge MCP server v${VERSION} running on stdio`);\n}\n\n// Start server when run directly\nconst isMainModule =\n import.meta.url === `file://${process.argv[1]}` ||\n process.argv[1]?.endsWith(\"/forge-mcp\") ||\n process.argv[1]?.endsWith(\"\\\\forge-mcp\");\n\nif (isMainModule) {\n startStdioServer().catch((error) => {\n console.error(\"Fatal error:\", error);\n process.exit(1);\n });\n}\n"],"mappings":";;;;;;;;;AAaA,SAAgB,oBAA2B;AACzC,QAAO,CAAC,GAAG,OAAO,GAAG,iBAAiB;;;;;AAMxC,SAAgB,oBAAoB,MAAwC;AAC1E,KAAI,CAAC,KAAK,YAAY,OAAO,KAAK,aAAa,YAAY,KAAK,SAAS,MAAM,CAAC,WAAW,EACzF,QAAO;EACL,SAAS,CACP;GACE,MAAM;GACN,MAAM;GACP,CACF;EACD,SAAS;EACV;AAGH,UAAS,KAAK,SAAS;AAEvB,QAAO,EACL,SAAS,CACP;EACE,MAAM;EACN,MAAM,KAAK,UACT;GACE,SAAS;GACT,SAAS;GACT,UAAU,MAAM,KAAK,SAAS,MAAM,GAAG;GACxC,EACD,MACA,EACD;EACF,CACF,EACF;;;;;AAMH,SAAgB,sBAAkC;CAChD,MAAM,QAAQ,UAAU;AAExB,QAAO,EACL,SAAS,CACP;EACE,MAAM;EACN,MAAM,KAAK,UACT;GACE,UAAU,QAAQ,MAAM,MAAM,MAAM,GAAG,KAAK;GAC5C,YAAY,CAAC,CAAC;GACf,EACD,MACA,EACD;EACF,CACF,EACF;;;;;AAMH,eAAsB,eACpB,MACA,MACqB;AACrB,KAAI,SAAS,kBACX,QAAO,oBAAoB,KAA6B;AAG1D,KAAI,SAAS,mBACX,QAAO,qBAAqB;CAI9B,MAAM,WAAW,UAAU;AAC3B,KAAI,CAAC,SACH,QAAO;EACL,SAAS,CACP;GACE,MAAM;GACN,MAAM;GACP,CACF;EACD,SAAS;EACV;AAGH,QAAO,2BAA2B,MAAM,MAAM,EAAE,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;ACxE7D,SAAgB,oBAA4B;CAC1C,MAAM,SAAS,IAAI,OACjB;EACE,MAAM;EACN,SAAS;EACV,EACD;EACE,cAAc,EACZ,OAAO,EAAE,EACV;EACD,cAAc;EACf,CACF;AAED,QAAO,kBAAkB,wBAAwB,YAAY;AAC3D,SAAO,EAAE,OAAO,mBAAmB,EAAE;GACrC;AAEF,QAAO,kBAAkB,uBAAuB,OAAO,YAAY;EACjE,MAAM,EAAE,MAAM,WAAW,SAAS,QAAQ;AAE1C,MAAI;AAEF,UADe,MAAM,eAAe,MAAO,QAAoC,EAAE,CAAC;WAE3E,OAAO;AAEd,UAAO;IACL,SAAS,CAAC;KAAE,MAAM;KAAiB,MAAM,UAF3B,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;KAEN,CAAC;IAC/D,SAAS;IACV;;GAEH;AAEF,QAAO;;;;;AAMT,eAAsB,mBAAkC;CACtD,MAAM,SAAS,mBAAmB;CAClC,MAAM,YAAY,IAAI,sBAAsB;AAC5C,OAAM,OAAO,QAAQ,UAAU;AAC/B,SAAQ,MAAM,qBAAqB,QAAQ,mBAAmB;;AAShE,IAJE,OAAO,KAAK,QAAQ,UAAU,QAAQ,KAAK,QAC3C,QAAQ,KAAK,IAAI,SAAS,aAAa,IACvC,QAAQ,KAAK,IAAI,SAAS,cAAc,CAGxC,mBAAkB,CAAC,OAAO,UAAU;AAClC,SAAQ,MAAM,gBAAgB,MAAM;AACpC,SAAQ,KAAK,EAAE;EACf"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/stdio.ts","../src/index.ts"],"sourcesContent":["import { getToken, setToken } from \"@studiometa/forge-api\";\n\nimport type { ToolResult } from \"./handlers/types.ts\";\n\nimport { executeToolWithCredentials } from \"./handlers/index.ts\";\nimport { getTools, STDIO_ONLY_TOOLS } from \"./tools.ts\";\nimport type { GetToolsOptions } from \"./tools.ts\";\n\nexport type { ToolResult };\n\n/**\n * Get all available tools (including stdio-only configuration tools).\n *\n * @param options - Optional filtering. When `readOnly` is true, forge_write is excluded.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function getAvailableTools(options?: GetToolsOptions): any[] {\n return [...getTools(options), ...STDIO_ONLY_TOOLS];\n}\n\n/**\n * Handle the forge_configure tool.\n */\nexport function handleConfigureTool(args: { apiToken: string }): ToolResult {\n if (!args.apiToken || typeof args.apiToken !== \"string\" || args.apiToken.trim().length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: \"Error: apiToken is required and must be a non-empty string.\",\n },\n ],\n structuredContent: {\n success: false,\n error: \"apiToken is required and must be a non-empty string.\",\n },\n isError: true,\n };\n }\n\n setToken(args.apiToken);\n\n const maskedToken = `***${args.apiToken.slice(-4)}`;\n const data = {\n success: true,\n message: \"Laravel Forge API token configured successfully\",\n apiToken: maskedToken,\n };\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(data, null, 2),\n },\n ],\n structuredContent: data,\n };\n}\n\n/**\n * Handle the forge_get_config tool.\n */\nexport function handleGetConfigTool(): ToolResult {\n const token = getToken();\n\n const data = {\n apiToken: token ? `***${token.slice(-4)}` : \"not configured\",\n configured: !!token,\n };\n\n return {\n content: [\n {\n type: \"text\",\n text: JSON.stringify(data, null, 2),\n },\n ],\n structuredContent: data,\n };\n}\n\n/**\n * Options for handleToolCall.\n */\nexport interface HandleToolCallOptions {\n /** When true, forge_write is rejected with an error. */\n readOnly?: boolean;\n}\n\n/**\n * Handle a tool call request.\n *\n * Routes to the appropriate handler based on tool name:\n * - forge_configure / forge_get_config — stdio-only config tools\n * - forge — read-only operations (list, get, help, schema)\n * - forge_write — write operations (create, update, delete, deploy, etc.)\n */\nexport async function handleToolCall(\n name: string,\n args: Record<string, unknown>,\n options?: HandleToolCallOptions,\n): Promise<ToolResult> {\n if (name === \"forge_configure\") {\n return handleConfigureTool(args as { apiToken: string });\n }\n\n if (name === \"forge_get_config\") {\n return handleGetConfigTool();\n }\n\n // Reject forge_write in read-only mode\n if (name === \"forge_write\" && options?.readOnly) {\n return {\n content: [\n {\n type: \"text\",\n text: \"Error: Server is running in read-only mode. Write operations are disabled.\",\n },\n ],\n structuredContent: {\n success: false,\n error: \"Server is running in read-only mode. Write operations are disabled.\",\n },\n isError: true,\n };\n }\n\n // Both forge and forge_write require authentication\n if (name === \"forge\" || name === \"forge_write\") {\n const apiToken = getToken();\n if (!apiToken) {\n return {\n content: [\n {\n type: \"text\",\n text: 'Error: Forge API token not configured. Use \"forge_configure\" tool or set FORGE_API_TOKEN environment variable.',\n },\n ],\n structuredContent: {\n success: false,\n error:\n 'Forge API token not configured. Use \"forge_configure\" tool or set FORGE_API_TOKEN environment variable.',\n },\n isError: true,\n };\n }\n\n return executeToolWithCredentials(name, args, { apiToken });\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: `Error: Unknown tool \"${name}\".`,\n },\n ],\n structuredContent: { success: false, error: `Unknown tool \"${name}\".` },\n isError: true,\n };\n}\n","#!/usr/bin/env node\n\n/**\n * Forge MCP Server — Stdio Transport\n *\n * This is the local execution mode using stdio transport.\n * For remote HTTP deployment, use server.ts instead.\n *\n * Usage:\n * npx @studiometa/forge-mcp\n * npx @studiometa/forge-mcp --read-only\n * FORGE_READ_ONLY=true npx @studiometa/forge-mcp\n *\n * Or in Claude Desktop config:\n * {\n * \"mcpServers\": {\n * \"forge\": {\n * \"command\": \"forge-mcp\",\n * \"args\": [\"--read-only\"],\n * \"env\": { \"FORGE_API_TOKEN\": \"your-token\" }\n * }\n * }\n * }\n */\n\nimport { Server } from \"@modelcontextprotocol/sdk/server/index.js\";\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { CallToolRequestSchema, ListToolsRequestSchema } from \"@modelcontextprotocol/sdk/types.js\";\n\nimport { parseReadOnlyFlag } from \"./flags.ts\";\nimport { INSTRUCTIONS } from \"./instructions.ts\";\nimport { getAvailableTools, handleToolCall } from \"./stdio.ts\";\nimport { VERSION } from \"./version.ts\";\n\n// Re-export so consumers can still import from the main entry point\nexport { parseReadOnlyFlag } from \"./flags.ts\";\n\n/**\n * Options for the stdio MCP server.\n */\nexport interface StdioServerOptions {\n /** When true, forge_write tool is not registered and write operations are rejected. */\n readOnly?: boolean;\n}\n\n/**\n * Create and configure the MCP server.\n */\nexport function createStdioServer(options?: StdioServerOptions): Server {\n const readOnly = options?.readOnly ?? false;\n\n const server = new Server(\n {\n name: \"forge-mcp\",\n version: VERSION,\n },\n {\n capabilities: {\n tools: {},\n },\n instructions: INSTRUCTIONS,\n },\n );\n\n server.setRequestHandler(ListToolsRequestSchema, async () => {\n return { tools: getAvailableTools({ readOnly }) };\n });\n\n server.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n try {\n const result = await handleToolCall(name, (args as Record<string, unknown>) ?? {}, {\n readOnly,\n });\n return result as unknown as Record<string, unknown>;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n return {\n content: [{ type: \"text\" as const, text: `Error: ${message}` }],\n structuredContent: { success: false, error: message },\n isError: true,\n };\n }\n });\n\n return server;\n}\n\n/**\n * Start the stdio server.\n */\nexport async function startStdioServer(options?: StdioServerOptions): Promise<void> {\n const server = createStdioServer(options);\n const transport = new StdioServerTransport();\n await server.connect(transport);\n const mode = options?.readOnly ? \" (read-only)\" : \"\";\n console.error(`Forge MCP server v${VERSION} running on stdio${mode}`);\n}\n\n// Start server when run directly\nconst isMainModule =\n import.meta.url === `file://${process.argv[1]}` ||\n process.argv[1]?.endsWith(\"/forge-mcp\") ||\n process.argv[1]?.endsWith(\"\\\\forge-mcp\");\n\nif (isMainModule) {\n const readOnly = parseReadOnlyFlag();\n startStdioServer({ readOnly }).catch((error) => {\n console.error(\"Fatal error:\", error);\n process.exit(1);\n });\n}\n"],"mappings":";;;;;;;;;;;;AAgBA,SAAgB,kBAAkB,SAAkC;AAClE,QAAO,CAAC,GAAG,SAAS,QAAQ,EAAE,GAAG,iBAAiB;;;;;AAMpD,SAAgB,oBAAoB,MAAwC;AAC1E,KAAI,CAAC,KAAK,YAAY,OAAO,KAAK,aAAa,YAAY,KAAK,SAAS,MAAM,CAAC,WAAW,EACzF,QAAO;EACL,SAAS,CACP;GACE,MAAM;GACN,MAAM;GACP,CACF;EACD,mBAAmB;GACjB,SAAS;GACT,OAAO;GACR;EACD,SAAS;EACV;AAGH,UAAS,KAAK,SAAS;CAGvB,MAAM,OAAO;EACX,SAAS;EACT,SAAS;EACT,UAJkB,MAAM,KAAK,SAAS,MAAM,GAAG;EAKhD;AAED,QAAO;EACL,SAAS,CACP;GACE,MAAM;GACN,MAAM,KAAK,UAAU,MAAM,MAAM,EAAE;GACpC,CACF;EACD,mBAAmB;EACpB;;;;;AAMH,SAAgB,sBAAkC;CAChD,MAAM,QAAQ,UAAU;CAExB,MAAM,OAAO;EACX,UAAU,QAAQ,MAAM,MAAM,MAAM,GAAG,KAAK;EAC5C,YAAY,CAAC,CAAC;EACf;AAED,QAAO;EACL,SAAS,CACP;GACE,MAAM;GACN,MAAM,KAAK,UAAU,MAAM,MAAM,EAAE;GACpC,CACF;EACD,mBAAmB;EACpB;;;;;;;;;;AAmBH,eAAsB,eACpB,MACA,MACA,SACqB;AACrB,KAAI,SAAS,kBACX,QAAO,oBAAoB,KAA6B;AAG1D,KAAI,SAAS,mBACX,QAAO,qBAAqB;AAI9B,KAAI,SAAS,iBAAiB,SAAS,SACrC,QAAO;EACL,SAAS,CACP;GACE,MAAM;GACN,MAAM;GACP,CACF;EACD,mBAAmB;GACjB,SAAS;GACT,OAAO;GACR;EACD,SAAS;EACV;AAIH,KAAI,SAAS,WAAW,SAAS,eAAe;EAC9C,MAAM,WAAW,UAAU;AAC3B,MAAI,CAAC,SACH,QAAO;GACL,SAAS,CACP;IACE,MAAM;IACN,MAAM;IACP,CACF;GACD,mBAAmB;IACjB,SAAS;IACT,OACE;IACH;GACD,SAAS;GACV;AAGH,SAAO,2BAA2B,MAAM,MAAM,EAAE,UAAU,CAAC;;AAG7D,QAAO;EACL,SAAS,CACP;GACE,MAAM;GACN,MAAM,wBAAwB,KAAK;GACpC,CACF;EACD,mBAAmB;GAAE,SAAS;GAAO,OAAO,iBAAiB,KAAK;GAAK;EACvE,SAAS;EACV;;;;;;;;;;;;;;;;;;;;;;;;;;;AChHH,SAAgB,kBAAkB,SAAsC;CACtE,MAAM,WAAW,SAAS,YAAY;CAEtC,MAAM,SAAS,IAAI,OACjB;EACE,MAAM;EACN,SAAS;EACV,EACD;EACE,cAAc,EACZ,OAAO,EAAE,EACV;EACD,cAAc;EACf,CACF;AAED,QAAO,kBAAkB,wBAAwB,YAAY;AAC3D,SAAO,EAAE,OAAO,kBAAkB,EAAE,UAAU,CAAC,EAAE;GACjD;AAEF,QAAO,kBAAkB,uBAAuB,OAAO,YAAY;EACjE,MAAM,EAAE,MAAM,WAAW,SAAS,QAAQ;AAE1C,MAAI;AAIF,UAHe,MAAM,eAAe,MAAO,QAAoC,EAAE,EAAE,EACjF,UACD,CAAC;WAEK,OAAO;GACd,MAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AACtE,UAAO;IACL,SAAS,CAAC;KAAE,MAAM;KAAiB,MAAM,UAAU;KAAW,CAAC;IAC/D,mBAAmB;KAAE,SAAS;KAAO,OAAO;KAAS;IACrD,SAAS;IACV;;GAEH;AAEF,QAAO;;;;;AAMT,eAAsB,iBAAiB,SAA6C;CAClF,MAAM,SAAS,kBAAkB,QAAQ;CACzC,MAAM,YAAY,IAAI,sBAAsB;AAC5C,OAAM,OAAO,QAAQ,UAAU;CAC/B,MAAM,OAAO,SAAS,WAAW,iBAAiB;AAClD,SAAQ,MAAM,qBAAqB,QAAQ,mBAAmB,OAAO;;AASvE,IAJE,OAAO,KAAK,QAAQ,UAAU,QAAQ,KAAK,QAC3C,QAAQ,KAAK,IAAI,SAAS,aAAa,IACvC,QAAQ,KAAK,IAAI,SAAS,cAAc,CAIxC,kBAAiB,EAAE,UADF,mBAAmB,EACP,CAAC,CAAC,OAAO,UAAU;AAC9C,SAAQ,MAAM,gBAAgB,MAAM;AACpC,SAAQ,KAAK,EAAE;EACf"}
|
package/dist/server.d.ts
CHANGED
|
@@ -9,6 +9,8 @@
|
|
|
9
9
|
*
|
|
10
10
|
* Usage:
|
|
11
11
|
* forge-mcp-server
|
|
12
|
+
* forge-mcp-server --read-only
|
|
13
|
+
* FORGE_READ_ONLY=true forge-mcp-server
|
|
12
14
|
* PORT=3000 forge-mcp-server
|
|
13
15
|
*
|
|
14
16
|
* Endpoints:
|
|
@@ -19,8 +21,15 @@
|
|
|
19
21
|
* GET /health - Health check
|
|
20
22
|
*/
|
|
21
23
|
import { type Server } from "node:http";
|
|
24
|
+
/**
|
|
25
|
+
* Options for the HTTP server.
|
|
26
|
+
*/
|
|
27
|
+
export interface HttpStartOptions {
|
|
28
|
+
/** When true, forge_write tool is not registered. */
|
|
29
|
+
readOnly?: boolean;
|
|
30
|
+
}
|
|
22
31
|
/**
|
|
23
32
|
* Start the HTTP server with Streamable HTTP transport.
|
|
24
33
|
*/
|
|
25
|
-
export declare function startHttpServer(port?: number, host?: string): Promise<Server>;
|
|
34
|
+
export declare function startHttpServer(port?: number, host?: string, options?: HttpStartOptions): Promise<Server>;
|
|
26
35
|
//# sourceMappingURL=server.d.ts.map
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AAEA
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;GAoBG;AAGH,OAAO,EAAgB,KAAK,MAAM,EAAE,MAAM,WAAW,CAAC;AAUtD;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,qDAAqD;IACrD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,IAAI,GAAE,MAAqB,EAC3B,IAAI,GAAE,MAAqB,EAC3B,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,MAAM,CAAC,CA8CjB"}
|
package/dist/server.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { t as
|
|
3
|
-
import {
|
|
2
|
+
import { t as parseReadOnlyFlag } from "./flags-LFbdErsZ.js";
|
|
3
|
+
import { t as VERSION } from "./version-BmEJceWJ.js";
|
|
4
|
+
import { a as SessionManager, n as createMcpRequestHandler, t as createHealthApp } from "./http-w0DliUHY.js";
|
|
4
5
|
import { toNodeHandler } from "h3/node";
|
|
5
6
|
import { createServer } from "node:http";
|
|
6
7
|
/**
|
|
@@ -13,6 +14,8 @@ import { createServer } from "node:http";
|
|
|
13
14
|
*
|
|
14
15
|
* Usage:
|
|
15
16
|
* forge-mcp-server
|
|
17
|
+
* forge-mcp-server --read-only
|
|
18
|
+
* FORGE_READ_ONLY=true forge-mcp-server
|
|
16
19
|
* PORT=3000 forge-mcp-server
|
|
17
20
|
*
|
|
18
21
|
* Endpoints:
|
|
@@ -27,9 +30,10 @@ var DEFAULT_HOST = "0.0.0.0";
|
|
|
27
30
|
/**
|
|
28
31
|
* Start the HTTP server with Streamable HTTP transport.
|
|
29
32
|
*/
|
|
30
|
-
function startHttpServer(port = DEFAULT_PORT, host = DEFAULT_HOST) {
|
|
33
|
+
function startHttpServer(port = DEFAULT_PORT, host = DEFAULT_HOST, options) {
|
|
31
34
|
return new Promise((resolve) => {
|
|
32
|
-
const
|
|
35
|
+
const readOnly = options?.readOnly ?? false;
|
|
36
|
+
const handleMcp = createMcpRequestHandler(new SessionManager(), { readOnly });
|
|
33
37
|
const healthHandler = toNodeHandler(createHealthApp());
|
|
34
38
|
const server = createServer(async (req, res) => {
|
|
35
39
|
const url = req.url ?? "/";
|
|
@@ -41,7 +45,8 @@ function startHttpServer(port = DEFAULT_PORT, host = DEFAULT_HOST) {
|
|
|
41
45
|
});
|
|
42
46
|
server.listen(port, host, () => {
|
|
43
47
|
const displayHost = host === "0.0.0.0" ? "localhost" : host;
|
|
44
|
-
|
|
48
|
+
const mode = readOnly ? " (read-only)" : "";
|
|
49
|
+
console.log(`Forge MCP server v${VERSION}${mode}`);
|
|
45
50
|
console.log(`Node.js ${process.version}`);
|
|
46
51
|
console.log("");
|
|
47
52
|
console.log(`Running at http://${displayHost}:${port}`);
|
|
@@ -53,12 +58,16 @@ function startHttpServer(port = DEFAULT_PORT, host = DEFAULT_HOST) {
|
|
|
53
58
|
console.log("Authentication:");
|
|
54
59
|
console.log(" Bearer token in Authorization header");
|
|
55
60
|
console.log(" Token format: your raw Forge API token");
|
|
61
|
+
if (readOnly) {
|
|
62
|
+
console.log("");
|
|
63
|
+
console.log("Mode: READ-ONLY (write operations disabled)");
|
|
64
|
+
}
|
|
56
65
|
console.log("");
|
|
57
66
|
resolve(server);
|
|
58
67
|
});
|
|
59
68
|
});
|
|
60
69
|
}
|
|
61
|
-
if (import.meta.url === `file://${process.argv[1]}` || process.argv[1]?.endsWith("/forge-mcp-server") || process.argv[1]?.endsWith("\\forge-mcp-server")) startHttpServer(Number.parseInt(process.env.PORT || String(DEFAULT_PORT), 10), process.env.HOST || DEFAULT_HOST).catch((error) => {
|
|
70
|
+
if (import.meta.url === `file://${process.argv[1]}` || process.argv[1]?.endsWith("/forge-mcp-server") || process.argv[1]?.endsWith("\\forge-mcp-server")) startHttpServer(Number.parseInt(process.env.PORT || String(DEFAULT_PORT), 10), process.env.HOST || DEFAULT_HOST, { readOnly: parseReadOnlyFlag() }).catch((error) => {
|
|
62
71
|
console.error("Fatal error:", error);
|
|
63
72
|
process.exit(1);
|
|
64
73
|
});
|
package/dist/server.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.js","names":[],"sources":["../src/server.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * Forge MCP Server - HTTP Transport (Streamable HTTP)\n *\n * Implements the official MCP Streamable HTTP transport specification.\n * Credentials are passed via Bearer token in the Authorization header.\n *\n * Token format: raw Forge API token\n *\n * Usage:\n * forge-mcp-server\n * PORT=3000 forge-mcp-server\n *\n * Endpoints:\n * POST /mcp - MCP Streamable HTTP (JSON-RPC messages)\n * GET /mcp - MCP Streamable HTTP (SSE stream for server notifications)\n * DELETE /mcp - MCP Streamable HTTP (session termination)\n * GET / - Service info\n * GET /health - Health check\n */\n\nimport { toNodeHandler } from \"h3/node\";\nimport { createServer, type Server } from \"node:http\";\n\nimport { createHealthApp, createMcpRequestHandler } from \"./http.ts\";\nimport { SessionManager } from \"./sessions.ts\";\nimport { VERSION } from \"./version.ts\";\n\nconst DEFAULT_PORT = 3000;\nconst DEFAULT_HOST = \"0.0.0.0\";\n\n/**\n * Start the HTTP server with Streamable HTTP transport.\n */\nexport function startHttpServer(\n port: number = DEFAULT_PORT,\n host: string = DEFAULT_HOST,\n): Promise<Server> {\n return new Promise((resolve) => {\n const sessions = new SessionManager();\n const handleMcp = createMcpRequestHandler(sessions);\n const healthApp = createHealthApp();\n const healthHandler = toNodeHandler(healthApp);\n\n const server = createServer(async (req, res) => {\n const url = req.url ?? \"/\";\n\n // Route /mcp to MCP Streamable HTTP transport\n if (url === \"/mcp\" || url.startsWith(\"/mcp?\")) {\n await handleMcp(req, res);\n return;\n }\n\n // Everything else goes to h3 (health checks, service info)\n healthHandler(req, res);\n });\n\n server.listen(port, host, () => {\n const displayHost = host === \"0.0.0.0\" ? \"localhost\" : host;\n console.log(`Forge MCP server v${VERSION}`);\n console.log(`Node.js ${process.version}`);\n console.log(\"\");\n console.log(`Running at http://${displayHost}:${port}`);\n console.log(\"\");\n console.log(\"Endpoints:\");\n console.log(\n ` POST/GET/DELETE http://${displayHost}:${port}/mcp - MCP Streamable HTTP endpoint`,\n );\n console.log(` GET http://${displayHost}:${port}/health - Health check`);\n console.log(\"\");\n console.log(\"Authentication:\");\n console.log(\" Bearer token in Authorization header\");\n console.log(\" Token format: your raw Forge API token\");\n console.log(\"\");\n resolve(server);\n });\n });\n}\n\n// Start server when run directly\nconst isMainModule =\n import.meta.url === `file://${process.argv[1]}` ||\n process.argv[1]?.endsWith(\"/forge-mcp-server\") ||\n process.argv[1]?.endsWith(\"\\\\forge-mcp-server\");\n\nif (isMainModule) {\n const port = Number.parseInt(process.env.PORT || String(DEFAULT_PORT), 10);\n const host = process.env.HOST || DEFAULT_HOST;\n\n startHttpServer(port, host).catch((error) => {\n console.error(\"Fatal error:\", error);\n process.exit(1);\n });\n}\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"server.js","names":[],"sources":["../src/server.ts"],"sourcesContent":["#!/usr/bin/env node\n\n/**\n * Forge MCP Server - HTTP Transport (Streamable HTTP)\n *\n * Implements the official MCP Streamable HTTP transport specification.\n * Credentials are passed via Bearer token in the Authorization header.\n *\n * Token format: raw Forge API token\n *\n * Usage:\n * forge-mcp-server\n * forge-mcp-server --read-only\n * FORGE_READ_ONLY=true forge-mcp-server\n * PORT=3000 forge-mcp-server\n *\n * Endpoints:\n * POST /mcp - MCP Streamable HTTP (JSON-RPC messages)\n * GET /mcp - MCP Streamable HTTP (SSE stream for server notifications)\n * DELETE /mcp - MCP Streamable HTTP (session termination)\n * GET / - Service info\n * GET /health - Health check\n */\n\nimport { toNodeHandler } from \"h3/node\";\nimport { createServer, type Server } from \"node:http\";\n\nimport { createHealthApp, createMcpRequestHandler } from \"./http.ts\";\nimport { parseReadOnlyFlag } from \"./flags.ts\";\nimport { SessionManager } from \"./sessions.ts\";\nimport { VERSION } from \"./version.ts\";\n\nconst DEFAULT_PORT = 3000;\nconst DEFAULT_HOST = \"0.0.0.0\";\n\n/**\n * Options for the HTTP server.\n */\nexport interface HttpStartOptions {\n /** When true, forge_write tool is not registered. */\n readOnly?: boolean;\n}\n\n/**\n * Start the HTTP server with Streamable HTTP transport.\n */\nexport function startHttpServer(\n port: number = DEFAULT_PORT,\n host: string = DEFAULT_HOST,\n options?: HttpStartOptions,\n): Promise<Server> {\n return new Promise((resolve) => {\n const readOnly = options?.readOnly ?? false;\n const sessions = new SessionManager();\n const handleMcp = createMcpRequestHandler(sessions, { readOnly });\n const healthApp = createHealthApp();\n const healthHandler = toNodeHandler(healthApp);\n\n const server = createServer(async (req, res) => {\n const url = req.url ?? \"/\";\n\n // Route /mcp to MCP Streamable HTTP transport\n if (url === \"/mcp\" || url.startsWith(\"/mcp?\")) {\n await handleMcp(req, res);\n return;\n }\n\n // Everything else goes to h3 (health checks, service info)\n healthHandler(req, res);\n });\n\n server.listen(port, host, () => {\n const displayHost = host === \"0.0.0.0\" ? \"localhost\" : host;\n const mode = readOnly ? \" (read-only)\" : \"\";\n console.log(`Forge MCP server v${VERSION}${mode}`);\n console.log(`Node.js ${process.version}`);\n console.log(\"\");\n console.log(`Running at http://${displayHost}:${port}`);\n console.log(\"\");\n console.log(\"Endpoints:\");\n console.log(\n ` POST/GET/DELETE http://${displayHost}:${port}/mcp - MCP Streamable HTTP endpoint`,\n );\n console.log(` GET http://${displayHost}:${port}/health - Health check`);\n console.log(\"\");\n console.log(\"Authentication:\");\n console.log(\" Bearer token in Authorization header\");\n console.log(\" Token format: your raw Forge API token\");\n if (readOnly) {\n console.log(\"\");\n console.log(\"Mode: READ-ONLY (write operations disabled)\");\n }\n console.log(\"\");\n resolve(server);\n });\n });\n}\n\n// Start server when run directly\nconst isMainModule =\n import.meta.url === `file://${process.argv[1]}` ||\n process.argv[1]?.endsWith(\"/forge-mcp-server\") ||\n process.argv[1]?.endsWith(\"\\\\forge-mcp-server\");\n\nif (isMainModule) {\n const port = Number.parseInt(process.env.PORT || String(DEFAULT_PORT), 10);\n const host = process.env.HOST || DEFAULT_HOST;\n const readOnly = parseReadOnlyFlag();\n\n startHttpServer(port, host, { readOnly }).catch((error) => {\n console.error(\"Fatal error:\", error);\n process.exit(1);\n });\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAgCA,IAAM,eAAe;AACrB,IAAM,eAAe;;;;AAarB,SAAgB,gBACd,OAAe,cACf,OAAe,cACf,SACiB;AACjB,QAAO,IAAI,SAAS,YAAY;EAC9B,MAAM,WAAW,SAAS,YAAY;EAEtC,MAAM,YAAY,wBADD,IAAI,gBAAgB,EACe,EAAE,UAAU,CAAC;EAEjE,MAAM,gBAAgB,cADJ,iBAAiB,CACW;EAE9C,MAAM,SAAS,aAAa,OAAO,KAAK,QAAQ;GAC9C,MAAM,MAAM,IAAI,OAAO;AAGvB,OAAI,QAAQ,UAAU,IAAI,WAAW,QAAQ,EAAE;AAC7C,UAAM,UAAU,KAAK,IAAI;AACzB;;AAIF,iBAAc,KAAK,IAAI;IACvB;AAEF,SAAO,OAAO,MAAM,YAAY;GAC9B,MAAM,cAAc,SAAS,YAAY,cAAc;GACvD,MAAM,OAAO,WAAW,iBAAiB;AACzC,WAAQ,IAAI,qBAAqB,UAAU,OAAO;AAClD,WAAQ,IAAI,WAAW,QAAQ,UAAU;AACzC,WAAQ,IAAI,GAAG;AACf,WAAQ,IAAI,qBAAqB,YAAY,GAAG,OAAO;AACvD,WAAQ,IAAI,GAAG;AACf,WAAQ,IAAI,aAAa;AACzB,WAAQ,IACN,4BAA4B,YAAY,GAAG,KAAK,qCACjD;AACD,WAAQ,IAAI,iBAAiB,YAAY,GAAG,KAAK,wBAAwB;AACzE,WAAQ,IAAI,GAAG;AACf,WAAQ,IAAI,kBAAkB;AAC9B,WAAQ,IAAI,yCAAyC;AACrD,WAAQ,IAAI,2CAA2C;AACvD,OAAI,UAAU;AACZ,YAAQ,IAAI,GAAG;AACf,YAAQ,IAAI,8CAA8C;;AAE5D,WAAQ,IAAI,GAAG;AACf,WAAQ,OAAO;IACf;GACF;;AASJ,IAJE,OAAO,KAAK,QAAQ,UAAU,QAAQ,KAAK,QAC3C,QAAQ,KAAK,IAAI,SAAS,oBAAoB,IAC9C,QAAQ,KAAK,IAAI,SAAS,qBAAqB,CAO/C,iBAJa,OAAO,SAAS,QAAQ,IAAI,QAAQ,OAAO,aAAa,EAAE,GAAG,EAC7D,QAAQ,IAAI,QAAQ,cAGL,EAAE,UAFb,mBAAmB,EAEI,CAAC,CAAC,OAAO,UAAU;AACzD,SAAQ,MAAM,gBAAgB,MAAM;AACpC,SAAQ,KAAK,EAAE;EACf"}
|
package/dist/stdio.d.ts
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import type { ToolResult } from "./handlers/types.ts";
|
|
2
|
+
import type { GetToolsOptions } from "./tools.ts";
|
|
2
3
|
export type { ToolResult };
|
|
3
4
|
/**
|
|
4
5
|
* Get all available tools (including stdio-only configuration tools).
|
|
6
|
+
*
|
|
7
|
+
* @param options - Optional filtering. When `readOnly` is true, forge_write is excluded.
|
|
5
8
|
*/
|
|
6
|
-
export declare function getAvailableTools(): any[];
|
|
9
|
+
export declare function getAvailableTools(options?: GetToolsOptions): any[];
|
|
7
10
|
/**
|
|
8
11
|
* Handle the forge_configure tool.
|
|
9
12
|
*/
|
|
@@ -14,8 +17,20 @@ export declare function handleConfigureTool(args: {
|
|
|
14
17
|
* Handle the forge_get_config tool.
|
|
15
18
|
*/
|
|
16
19
|
export declare function handleGetConfigTool(): ToolResult;
|
|
20
|
+
/**
|
|
21
|
+
* Options for handleToolCall.
|
|
22
|
+
*/
|
|
23
|
+
export interface HandleToolCallOptions {
|
|
24
|
+
/** When true, forge_write is rejected with an error. */
|
|
25
|
+
readOnly?: boolean;
|
|
26
|
+
}
|
|
17
27
|
/**
|
|
18
28
|
* Handle a tool call request.
|
|
29
|
+
*
|
|
30
|
+
* Routes to the appropriate handler based on tool name:
|
|
31
|
+
* - forge_configure / forge_get_config — stdio-only config tools
|
|
32
|
+
* - forge — read-only operations (list, get, help, schema)
|
|
33
|
+
* - forge_write — write operations (create, update, delete, deploy, etc.)
|
|
19
34
|
*/
|
|
20
|
-
export declare function handleToolCall(name: string, args: Record<string, unknown
|
|
35
|
+
export declare function handleToolCall(name: string, args: Record<string, unknown>, options?: HandleToolCallOptions): Promise<ToolResult>;
|
|
21
36
|
//# sourceMappingURL=stdio.d.ts.map
|
package/dist/stdio.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"stdio.d.ts","sourceRoot":"","sources":["../src/stdio.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAItD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,YAAY,EAAE,UAAU,EAAE,CAAC;AAE3B;;;;GAIG;AAEH,wBAAgB,iBAAiB,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,GAAG,EAAE,CAElE;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAA;CAAE,GAAG,UAAU,CAmC1E;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,UAAU,CAiBhD;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,wDAAwD;IACxD,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;;;GAOG;AACH,wBAAsB,cAAc,CAClC,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,OAAO,CAAC,EAAE,qBAAqB,GAC9B,OAAO,CAAC,UAAU,CAAC,CA2DrB"}
|
package/dist/tools.d.ts
CHANGED
|
@@ -1,22 +1,47 @@
|
|
|
1
|
+
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
|
|
1
2
|
/**
|
|
2
|
-
*
|
|
3
|
+
* Read-only actions — safe operations that don't modify server state.
|
|
3
4
|
*/
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
export declare const READ_ACTIONS: readonly ["list", "get", "help", "schema"];
|
|
6
|
+
/**
|
|
7
|
+
* Write actions — operations that modify server state.
|
|
8
|
+
* These are separated into the `forge_write` tool for safety.
|
|
9
|
+
*/
|
|
10
|
+
export declare const WRITE_ACTIONS: readonly ["create", "update", "delete", "deploy", "reboot", "restart", "activate", "run"];
|
|
11
|
+
export type ReadAction = (typeof READ_ACTIONS)[number];
|
|
12
|
+
export type WriteAction = (typeof WRITE_ACTIONS)[number];
|
|
13
|
+
/**
|
|
14
|
+
* Check if an action is a write action.
|
|
15
|
+
*/
|
|
16
|
+
export declare function isWriteAction(action: string): action is WriteAction;
|
|
17
|
+
/**
|
|
18
|
+
* Check if an action is a read action.
|
|
19
|
+
*/
|
|
20
|
+
export declare function isReadAction(action: string): action is ReadAction;
|
|
10
21
|
/**
|
|
11
|
-
*
|
|
22
|
+
* Core tools available in both stdio and HTTP transports.
|
|
12
23
|
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
24
|
+
* Split into two tools for safety:
|
|
25
|
+
* - `forge` — read-only operations (auto-approvable by MCP clients)
|
|
26
|
+
* - `forge_write` — write operations (always requires confirmation)
|
|
15
27
|
*/
|
|
16
28
|
export declare const TOOLS: Tool[];
|
|
29
|
+
/**
|
|
30
|
+
* Options for filtering available tools.
|
|
31
|
+
*/
|
|
32
|
+
export interface GetToolsOptions {
|
|
33
|
+
/** When true, only read-only tools are returned (forge_write is excluded). */
|
|
34
|
+
readOnly?: boolean;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Get the list of core tools, optionally filtered.
|
|
38
|
+
*
|
|
39
|
+
* In read-only mode, forge_write is excluded entirely — it won't appear
|
|
40
|
+
* in the tool listing and cannot be called.
|
|
41
|
+
*/
|
|
42
|
+
export declare function getTools(options?: GetToolsOptions): Tool[];
|
|
17
43
|
/**
|
|
18
44
|
* Additional tools only available in stdio mode.
|
|
19
45
|
*/
|
|
20
46
|
export declare const STDIO_ONLY_TOOLS: Tool[];
|
|
21
|
-
export {};
|
|
22
47
|
//# sourceMappingURL=tools.d.ts.map
|
package/dist/tools.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,oCAAoC,CAAC;AAI/D;;GAEG;AACH,eAAO,MAAM,YAAY,4CAA6C,CAAC;AAEvE;;;GAGG;AACH,eAAO,MAAM,aAAa,2FAShB,CAAC;AAEX,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AACvD,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAC;AAEzD;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,IAAI,WAAW,CAEnE;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,IAAI,UAAU,CAEjE;AA0OD;;;;;;GAMG;AACH,eAAO,MAAM,KAAK,EAAE,IAAI,EAAwC,CAAC;AAEjE;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,OAAO,CAAC;CACpB;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,GAAG,IAAI,EAAE,CAK1D;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB,EAAE,IAAI,EAsDlC,CAAC"}
|