Skip to content

Commit 06bd5d2

Browse files
committed
feat: MCP-mode Dockerfile; handshake version read from package.json
1 parent dc167c8 commit 06bd5d2

3 files changed

Lines changed: 78 additions & 3 deletions

File tree

‎Dockerfile.mcp‎

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# DocForge — MCP server image (stdio).
2+
#
3+
# Runs dist/index.js (the MCP stdio server) directly — no Slack, no HTTP.
4+
# This is the image MCP directories (e.g. Glama) use to start the server and
5+
# probe it with introspection requests. For the Slack Agent delivery image,
6+
# see ./Dockerfile.
7+
8+
# ---- build stage ---------------------------------------------------------
9+
FROM node:22-bookworm-slim AS build
10+
WORKDIR /app
11+
12+
COPY package.json package-lock.json ./
13+
RUN npm ci
14+
15+
COPY tsconfig.json ./
16+
COPY src ./src
17+
RUN npm run build
18+
19+
# ---- runtime stage -------------------------------------------------------
20+
FROM node:22-bookworm-slim AS runtime
21+
WORKDIR /app
22+
ENV NODE_ENV=production
23+
24+
# Pinned Typst (same version/asset as CI).
25+
ARG TYPST_VERSION=0.14.2
26+
ARG TYPST_SHA256=a6044cbad2a954deb921167e257e120ac0a16b20339ec01121194ff9d394996d
27+
RUN apt-get update \
28+
&& apt-get install -y --no-install-recommends curl xz-utils ca-certificates \
29+
&& curl -fsSL -o /tmp/typst.tar.xz "https://github.com/typst/typst/releases/download/v${TYPST_VERSION}/typst-x86_64-unknown-linux-musl.tar.xz" \
30+
&& echo "${TYPST_SHA256} /tmp/typst.tar.xz" | sha256sum -c - \
31+
&& tar -xJf /tmp/typst.tar.xz -C /tmp \
32+
&& mv /tmp/typst-x86_64-unknown-linux-musl/typst /usr/local/bin/typst \
33+
&& chmod +x /usr/local/bin/typst \
34+
&& apt-get purge -y curl xz-utils && apt-get autoremove -y \
35+
&& rm -rf /var/lib/apt/lists/* /tmp/typst-* /tmp/typst.tar.xz \
36+
&& typst --version
37+
38+
# Production dependencies only.
39+
COPY package.json package-lock.json ./
40+
RUN npm ci --omit=dev && npm cache clean --force
41+
42+
# Build output + runtime assets.
43+
COPY --from=build /app/dist ./dist
44+
COPY templates ./templates
45+
COPY brand_kits ./brand_kits
46+
COPY packages ./packages
47+
COPY marketplace ./marketplace
48+
COPY vendor ./vendor
49+
COPY scripts ./scripts
50+
51+
# Fail the build if Typst is below pin or a vendored package is missing.
52+
RUN node scripts/check-typst-version.mjs \
53+
&& node scripts/check-vendored-packages.mjs
54+
55+
# Persisted document workspaces.
56+
ENV DOCFORGE_DATA_ROOT=/data/documents
57+
RUN mkdir -p /data/documents
58+
VOLUME ["/data"]
59+
60+
# MCP server over stdio.
61+
CMD ["node", "dist/index.js"]

‎src/forge/mcp-client.ts‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,15 @@
11
import path from "node:path";
22
import { access } from "node:fs/promises";
33
import { fileURLToPath } from "node:url";
4+
import { readFileSync } from "node:fs";
45
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
56
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
6-
import { getDataRoot } from "../config.js";
7+
import { getDataRoot, PACKAGE_ROOT } from "../config.js";
8+
9+
// Version from package.json so the client identity can't drift from the release.
10+
const PACKAGE_VERSION: string = JSON.parse(
11+
readFileSync(path.join(PACKAGE_ROOT, "package.json"), "utf8"),
12+
).version;
713
import type { Diagnostic } from "../errors.js";
814
import type { LintIssue } from "../lint/engine.js";
915
import type { VisualQAFinding } from "../qa/visual.js";
@@ -120,7 +126,7 @@ export class DocForgeMcpClient {
120126
args: [this.serverEntry],
121127
env: { ...process.env } as Record<string, string>,
122128
});
123-
const client = new Client({ name: "forge-slack-agent", version: "0.7.0" });
129+
const client = new Client({ name: "forge-slack-agent", version: PACKAGE_VERSION });
124130
// If the child dies, drop our handles so the next call respawns.
125131
transport.onclose = () => {
126132
if (this.transport === transport) {

‎src/mcp/server.ts‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,18 @@
1+
import { readFileSync } from "node:fs";
2+
import path from "node:path";
13
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
24
import type { CallToolResult } from "@modelcontextprotocol/sdk/types.js";
35
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
46
import { z } from "zod";
7+
import { PACKAGE_ROOT } from "../config.js";
58
import * as service from "../service.js";
69
import { getTemplateReadme, getTemplateSample } from "../templates/registry.js";
710

11+
// Version from package.json so the MCP handshake can't drift from the release.
12+
const PACKAGE_VERSION: string = JSON.parse(
13+
readFileSync(path.join(PACKAGE_ROOT, "package.json"), "utf8"),
14+
).version;
15+
816
const TOOL_DESCRIPTION_CREATE = `Creates a document from template + validated JSON data.
917
Returns document_id (required for compile, preview, export). Expires after 24h idle.
1018
WHEN TO USE: After docforge_get_template_schema, when structured data is ready.
@@ -13,7 +21,7 @@ ON FAILURE: schema_error with agent_action — fix fields before compile.`;
1321
export async function startMcpServer(): Promise<void> {
1422
await service.initService();
1523

16-
const server = new McpServer({ name: "docforge", version: "0.7.0" });
24+
const server = new McpServer({ name: "docforge", version: PACKAGE_VERSION });
1725

1826
server.registerTool(
1927
"docforge_list_templates",

0 commit comments

Comments
 (0)