@thecorporation/mcp-server 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,82 @@
1
+ # The Corporation — MCP Server
2
+
3
+ <!-- mcp-name: io.github.thecorporation/corporate-os -->
4
+
5
+ Your agent just formed an LLC in 4 tool calls.
6
+
7
+ An MCP server that gives AI agents full corporate operations capabilities — entity formation, equity management, payroll, contracts, banking, and tax compliance. 12 tools, zero corporate law expertise required.
8
+
9
+ ## Install
10
+
11
+ ```bash
12
+ uv pip install thecorporation
13
+ ```
14
+
15
+ Or run directly:
16
+
17
+ ```bash
18
+ uvx thecorporation
19
+ ```
20
+
21
+ ## Configure with Claude Desktop
22
+
23
+ Add to your `claude_desktop_config.json`:
24
+
25
+ ```json
26
+ {
27
+ "mcpServers": {
28
+ "thecorporation": {
29
+ "command": "uvx",
30
+ "args": ["thecorporation"]
31
+ }
32
+ }
33
+ }
34
+ ```
35
+
36
+ ## Tools
37
+
38
+ | Tool | What it does |
39
+ |------|-------------|
40
+ | `form_entity` | Form an LLC or corporation in any US state |
41
+ | `issue_equity` | Issue shares or membership units to recipients |
42
+ | `issue_safe` | Issue SAFE notes (pre-money, post-money, MFN) to investors |
43
+ | `create_invoice` | Create and send invoices to customers |
44
+ | `run_payroll` | Run payroll for employees and contractors |
45
+ | `submit_payment` | Send payments via ACH, wire, or check |
46
+ | `get_signing_link` | Generate a signing link — documents can only be signed by humans via this URL |
47
+ | `convene_meeting` | Convene board, shareholder, or member meetings |
48
+ | `cast_vote` | Cast votes on meeting agenda items |
49
+ | `open_bank_account` | Open a business bank account (auto-assembles KYB) |
50
+ | `generate_contract` | Generate NDAs, contractor agreements, offer letters |
51
+ | `file_tax_document` | Generate 1099s, K-1s, estimated tax filings |
52
+
53
+ ## Example Prompts
54
+
55
+ - "Form a Delaware LLC called Acme AI for my consulting business"
56
+ - "Issue a $500K post-money SAFE with a $10M cap to Jane Smith"
57
+ - "Generate an NDA between my company and Acme Corp"
58
+ - "Run payroll for January 2025"
59
+ - "File 1099-NECs for all my contractors for tax year 2025"
60
+
61
+ ## How It Works
62
+
63
+ Every operation flows through a deterministic governance kernel:
64
+
65
+ ```
66
+ Agent Tool Call → Intent → Policy Evaluation → Execution → Receipt
67
+ ```
68
+
69
+ - No side effect without an auditable intent
70
+ - Every receipt is hash-bound and immutable
71
+ - Human obligations auto-generated for legally required signatures
72
+ - Double-entry ledger tracks every dollar
73
+
74
+ ## What This Is (and Isn't)
75
+
76
+ **This is** infrastructure for AI agents that need to operate businesses. It handles the corporate law, tax compliance, and financial plumbing so your agent doesn't have to.
77
+
78
+ **This is not** legal advice, tax advice, or a substitute for professional counsel. The platform computes from data it already has (K-1 allocations, 1099 aggregations, estimated taxes) but doesn't interpret tax law.
79
+
80
+ ## License
81
+
82
+ Apache 2.0
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/index.js ADDED
@@ -0,0 +1,124 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/index.ts
4
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
5
+ import { CorpAPIClient as CorpAPIClient2 } from "@thecorporation/corp-tools";
6
+
7
+ // src/server.ts
8
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
9
+ import { z } from "zod";
10
+ import {
11
+ GENERATED_TOOL_DEFINITIONS,
12
+ executeTool
13
+ } from "@thecorporation/corp-tools";
14
+ import { join } from "path";
15
+ import { homedir } from "os";
16
+ function buildZodShape(fn) {
17
+ const shape = {};
18
+ const props = fn.parameters.properties || {};
19
+ const required = new Set(fn.parameters.required || []);
20
+ for (const [pname, pinfo] of Object.entries(props)) {
21
+ let schema;
22
+ switch (pinfo.type) {
23
+ case "integer":
24
+ schema = z.number().int();
25
+ break;
26
+ case "number":
27
+ schema = z.number();
28
+ break;
29
+ case "boolean":
30
+ schema = z.boolean();
31
+ break;
32
+ case "array":
33
+ schema = z.array(pinfo.items?.type === "object" ? z.record(z.unknown()) : z.string());
34
+ break;
35
+ case "object":
36
+ schema = z.record(z.unknown());
37
+ break;
38
+ default:
39
+ schema = pinfo.enum ? z.enum(pinfo.enum) : z.string();
40
+ }
41
+ if (pinfo.description) schema = schema.describe(pinfo.description);
42
+ if (!required.has(pname)) schema = schema.optional();
43
+ shape[pname] = schema;
44
+ }
45
+ return shape;
46
+ }
47
+ function createMcpServer(client) {
48
+ const server = new McpServer({
49
+ name: "thecorporation",
50
+ version: "0.1.0"
51
+ });
52
+ const ctx = {
53
+ dataDir: join(homedir(), ".corp", "data")
54
+ };
55
+ for (const td of GENERATED_TOOL_DEFINITIONS) {
56
+ const fn = td.function;
57
+ const shape = buildZodShape(fn);
58
+ server.tool(fn.name, fn.description, shape, async (args) => {
59
+ const result = await executeTool(fn.name, args, client, ctx);
60
+ return { content: [{ type: "text", text: result }] };
61
+ });
62
+ }
63
+ return server;
64
+ }
65
+
66
+ // src/auth.ts
67
+ import { readFileSync, writeFileSync, mkdirSync } from "fs";
68
+ import { join as join2 } from "path";
69
+ import { homedir as homedir2 } from "os";
70
+ var CONFIG_FILE = join2(homedir2(), ".corp", "config.json");
71
+ function loadConfig() {
72
+ try {
73
+ return JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
74
+ } catch {
75
+ return {};
76
+ }
77
+ }
78
+ function saveConfig(cfg) {
79
+ const dir = join2(homedir2(), ".corp");
80
+ mkdirSync(dir, { recursive: true });
81
+ writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2) + "\n");
82
+ }
83
+ async function provision(apiUrl) {
84
+ const resp = await fetch(`${apiUrl.replace(/\/+$/, "")}/v1/workspaces/provision`, {
85
+ method: "POST",
86
+ headers: { "Content-Type": "application/json" },
87
+ body: JSON.stringify({ name: "mcp-auto" }),
88
+ signal: AbortSignal.timeout(15e3)
89
+ });
90
+ if (!resp.ok) throw new Error(`Provision failed: ${resp.status}`);
91
+ return await resp.json();
92
+ }
93
+ async function resolveOrProvisionAuth(apiUrl = "https://api.thecorporation.ai") {
94
+ const envKey = process.env.CORP_API_KEY || "";
95
+ const envWs = process.env.CORP_WORKSPACE_ID || "";
96
+ if (envKey && envWs) {
97
+ return { workspaceId: envWs, apiKey: envKey, scopes: ["*"] };
98
+ }
99
+ const cfg = loadConfig();
100
+ if (cfg.api_key && cfg.workspace_id) {
101
+ return { workspaceId: cfg.workspace_id, apiKey: cfg.api_key, scopes: ["*"] };
102
+ }
103
+ const result = await provision(apiUrl);
104
+ cfg.api_key = result.api_key;
105
+ cfg.workspace_id = result.workspace_id;
106
+ if (!cfg.api_url) cfg.api_url = apiUrl;
107
+ saveConfig(cfg);
108
+ return { workspaceId: result.workspace_id, apiKey: result.api_key, scopes: ["*"] };
109
+ }
110
+
111
+ // src/index.ts
112
+ async function main() {
113
+ const apiUrl = process.env.CORP_API_URL || "https://api.thecorporation.ai";
114
+ const ctx = await resolveOrProvisionAuth(apiUrl);
115
+ const client = new CorpAPIClient2(apiUrl, ctx.apiKey, ctx.workspaceId);
116
+ const server = createMcpServer(client);
117
+ const transport = new StdioServerTransport();
118
+ await server.connect(transport);
119
+ }
120
+ main().catch((err) => {
121
+ console.error("Fatal:", err);
122
+ process.exit(1);
123
+ });
124
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/server.ts","../src/auth.ts"],"sourcesContent":["/**\n * Entry point — resolve auth, start MCP server on stdio.\n */\n\nimport { StdioServerTransport } from \"@modelcontextprotocol/sdk/server/stdio.js\";\nimport { CorpAPIClient } from \"@thecorporation/corp-tools\";\nimport { createMcpServer } from \"./server.js\";\nimport { resolveOrProvisionAuth } from \"./auth.js\";\n\nasync function main(): Promise<void> {\n const apiUrl = process.env.CORP_API_URL || \"https://api.thecorporation.ai\";\n\n const ctx = await resolveOrProvisionAuth(apiUrl);\n const client = new CorpAPIClient(apiUrl, ctx.apiKey, ctx.workspaceId);\n const server = createMcpServer(client);\n const transport = new StdioServerTransport();\n await server.connect(transport);\n}\n\nmain().catch((err) => {\n console.error(\"Fatal:\", err);\n process.exit(1);\n});\n","/**\n * MCP server — registers all tools from corp-tools definitions,\n * dispatches to the backend API via CorpAPIClient.\n */\n\nimport { McpServer } from \"@modelcontextprotocol/sdk/server/mcp.js\";\nimport { z } from \"zod\";\nimport {\n GENERATED_TOOL_DEFINITIONS,\n CorpAPIClient,\n executeTool,\n type ToolContext,\n} from \"@thecorporation/corp-tools\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\n// ---------------------------------------------------------------------------\n// Build Zod schemas from JSON Schema properties\n// ---------------------------------------------------------------------------\n\ninterface ToolFunction {\n name: string;\n description: string;\n parameters: {\n type: string;\n properties: Record<string, { type: string; description?: string; enum?: string[]; items?: { type: string } }>;\n required: string[];\n };\n}\n\ninterface ToolDef {\n type: string;\n function: ToolFunction;\n}\n\nfunction buildZodShape(fn: ToolFunction): Record<string, z.ZodTypeAny> {\n const shape: Record<string, z.ZodTypeAny> = {};\n const props = fn.parameters.properties || {};\n const required = new Set(fn.parameters.required || []);\n\n for (const [pname, pinfo] of Object.entries(props)) {\n let schema: z.ZodTypeAny;\n switch (pinfo.type) {\n case \"integer\":\n schema = z.number().int();\n break;\n case \"number\":\n schema = z.number();\n break;\n case \"boolean\":\n schema = z.boolean();\n break;\n case \"array\":\n schema = z.array(pinfo.items?.type === \"object\" ? z.record(z.unknown()) : z.string());\n break;\n case \"object\":\n schema = z.record(z.unknown());\n break;\n default:\n schema = pinfo.enum ? z.enum(pinfo.enum as [string, ...string[]]) : z.string();\n }\n if (pinfo.description) schema = schema.describe(pinfo.description);\n if (!required.has(pname)) schema = schema.optional();\n shape[pname] = schema;\n }\n return shape;\n}\n\n// ---------------------------------------------------------------------------\n// Create and configure MCP server\n// ---------------------------------------------------------------------------\n\nexport function createMcpServer(client: CorpAPIClient): McpServer {\n const server = new McpServer({\n name: \"thecorporation\",\n version: \"0.1.0\",\n });\n\n const ctx: ToolContext = {\n dataDir: join(homedir(), \".corp\", \"data\"),\n };\n\n for (const td of GENERATED_TOOL_DEFINITIONS as unknown as ToolDef[]) {\n const fn = td.function;\n const shape = buildZodShape(fn);\n\n server.tool(fn.name, fn.description, shape, async (args) => {\n const result = await executeTool(fn.name, args as Record<string, unknown>, client, ctx);\n return { content: [{ type: \"text\" as const, text: result }] };\n });\n }\n\n return server;\n}\n","/**\n * MCP server authentication — auto-provisioning and shared config.\n *\n * Resolution order:\n * 1. CORP_API_KEY + CORP_WORKSPACE_ID env vars (explicit)\n * 2. ~/.corp/config.json (shared with TUI/CLI)\n * 3. Auto-provision via POST /v1/workspaces/provision\n */\n\nimport { readFileSync, writeFileSync, mkdirSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { homedir } from \"node:os\";\n\nexport interface McpAuthContext {\n workspaceId: string;\n apiKey: string;\n scopes: string[];\n}\n\nconst CONFIG_FILE = join(homedir(), \".corp\", \"config.json\");\n\nfunction loadConfig(): Record<string, string> {\n try {\n return JSON.parse(readFileSync(CONFIG_FILE, \"utf-8\"));\n } catch {\n return {};\n }\n}\n\nfunction saveConfig(cfg: Record<string, string>): void {\n const dir = join(homedir(), \".corp\");\n mkdirSync(dir, { recursive: true });\n writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2) + \"\\n\");\n}\n\nasync function provision(apiUrl: string): Promise<Record<string, string>> {\n const resp = await fetch(`${apiUrl.replace(/\\/+$/, \"\")}/v1/workspaces/provision`, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ name: \"mcp-auto\" }),\n signal: AbortSignal.timeout(15_000),\n });\n if (!resp.ok) throw new Error(`Provision failed: ${resp.status}`);\n return await resp.json() as Record<string, string>;\n}\n\nexport async function resolveOrProvisionAuth(\n apiUrl: string = \"https://api.thecorporation.ai\",\n): Promise<McpAuthContext> {\n // 1. Env vars\n const envKey = process.env.CORP_API_KEY || \"\";\n const envWs = process.env.CORP_WORKSPACE_ID || \"\";\n if (envKey && envWs) {\n return { workspaceId: envWs, apiKey: envKey, scopes: [\"*\"] };\n }\n\n // 2. Config file\n const cfg = loadConfig();\n if (cfg.api_key && cfg.workspace_id) {\n return { workspaceId: cfg.workspace_id, apiKey: cfg.api_key, scopes: [\"*\"] };\n }\n\n // 3. Auto-provision\n const result = await provision(apiUrl);\n cfg.api_key = result.api_key;\n cfg.workspace_id = result.workspace_id;\n if (!cfg.api_url) cfg.api_url = apiUrl;\n saveConfig(cfg);\n\n return { workspaceId: result.workspace_id, apiKey: result.api_key, scopes: [\"*\"] };\n}\n"],"mappings":"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","names":["CorpAPIClient","join","homedir","CorpAPIClient"]}
package/package.json ADDED
@@ -0,0 +1,31 @@
1
+ {
2
+ "name": "@thecorporation/mcp-server",
3
+ "version": "0.1.0",
4
+ "description": "MCP server for agent-native corporate operations",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "bin": {
8
+ "thecorporation": "./dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsup",
12
+ "dev": "tsup --watch"
13
+ },
14
+ "dependencies": {
15
+ "@modelcontextprotocol/sdk": "^1.12.0",
16
+ "@thecorporation/corp-tools": "0.1.0",
17
+ "zod": "^3.24.0"
18
+ },
19
+ "devDependencies": {
20
+ "tsup": "^8.4.0",
21
+ "typescript": "^5.8.0",
22
+ "@types/node": "^22.0.0"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "engines": {
28
+ "node": ">=20"
29
+ },
30
+ "license": "Apache-2.0"
31
+ }
package/server.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "io.github.thecorporation/corporate-os",
4
+ "title": "The Corporation",
5
+ "description": "Agent-native corporate operations — form LLCs, manage equity, run payroll, generate contracts, file taxes. 12 MCP tools, zero corporate law expertise required.",
6
+ "version": "0.1.0",
7
+ "repository": {
8
+ "url": "https://github.com/thecorporationai/thecorporation-mono",
9
+ "source": "github"
10
+ },
11
+ "packages": [
12
+ {
13
+ "registryType": "pypi",
14
+ "identifier": "thecorporation",
15
+ "version": "0.1.0",
16
+ "transport": {
17
+ "type": "stdio"
18
+ }
19
+ }
20
+ ]
21
+ }
package/smithery.yaml ADDED
@@ -0,0 +1,10 @@
1
+ startCommand:
2
+ type: stdio
3
+ configSchema:
4
+ type: object
5
+ properties: {}
6
+ commandFunction: |-
7
+ (config) => ({
8
+ command: "uvx",
9
+ args: ["thecorporation"]
10
+ })
package/src/auth.ts ADDED
@@ -0,0 +1,71 @@
1
+ /**
2
+ * MCP server authentication — auto-provisioning and shared config.
3
+ *
4
+ * Resolution order:
5
+ * 1. CORP_API_KEY + CORP_WORKSPACE_ID env vars (explicit)
6
+ * 2. ~/.corp/config.json (shared with TUI/CLI)
7
+ * 3. Auto-provision via POST /v1/workspaces/provision
8
+ */
9
+
10
+ import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
11
+ import { join } from "node:path";
12
+ import { homedir } from "node:os";
13
+
14
+ export interface McpAuthContext {
15
+ workspaceId: string;
16
+ apiKey: string;
17
+ scopes: string[];
18
+ }
19
+
20
+ const CONFIG_FILE = join(homedir(), ".corp", "config.json");
21
+
22
+ function loadConfig(): Record<string, string> {
23
+ try {
24
+ return JSON.parse(readFileSync(CONFIG_FILE, "utf-8"));
25
+ } catch {
26
+ return {};
27
+ }
28
+ }
29
+
30
+ function saveConfig(cfg: Record<string, string>): void {
31
+ const dir = join(homedir(), ".corp");
32
+ mkdirSync(dir, { recursive: true });
33
+ writeFileSync(CONFIG_FILE, JSON.stringify(cfg, null, 2) + "\n");
34
+ }
35
+
36
+ async function provision(apiUrl: string): Promise<Record<string, string>> {
37
+ const resp = await fetch(`${apiUrl.replace(/\/+$/, "")}/v1/workspaces/provision`, {
38
+ method: "POST",
39
+ headers: { "Content-Type": "application/json" },
40
+ body: JSON.stringify({ name: "mcp-auto" }),
41
+ signal: AbortSignal.timeout(15_000),
42
+ });
43
+ if (!resp.ok) throw new Error(`Provision failed: ${resp.status}`);
44
+ return await resp.json() as Record<string, string>;
45
+ }
46
+
47
+ export async function resolveOrProvisionAuth(
48
+ apiUrl: string = "https://api.thecorporation.ai",
49
+ ): Promise<McpAuthContext> {
50
+ // 1. Env vars
51
+ const envKey = process.env.CORP_API_KEY || "";
52
+ const envWs = process.env.CORP_WORKSPACE_ID || "";
53
+ if (envKey && envWs) {
54
+ return { workspaceId: envWs, apiKey: envKey, scopes: ["*"] };
55
+ }
56
+
57
+ // 2. Config file
58
+ const cfg = loadConfig();
59
+ if (cfg.api_key && cfg.workspace_id) {
60
+ return { workspaceId: cfg.workspace_id, apiKey: cfg.api_key, scopes: ["*"] };
61
+ }
62
+
63
+ // 3. Auto-provision
64
+ const result = await provision(apiUrl);
65
+ cfg.api_key = result.api_key;
66
+ cfg.workspace_id = result.workspace_id;
67
+ if (!cfg.api_url) cfg.api_url = apiUrl;
68
+ saveConfig(cfg);
69
+
70
+ return { workspaceId: result.workspace_id, apiKey: result.api_key, scopes: ["*"] };
71
+ }
package/src/index.ts ADDED
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Entry point — resolve auth, start MCP server on stdio.
3
+ */
4
+
5
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
+ import { CorpAPIClient } from "@thecorporation/corp-tools";
7
+ import { createMcpServer } from "./server.js";
8
+ import { resolveOrProvisionAuth } from "./auth.js";
9
+
10
+ async function main(): Promise<void> {
11
+ const apiUrl = process.env.CORP_API_URL || "https://api.thecorporation.ai";
12
+
13
+ const ctx = await resolveOrProvisionAuth(apiUrl);
14
+ const client = new CorpAPIClient(apiUrl, ctx.apiKey, ctx.workspaceId);
15
+ const server = createMcpServer(client);
16
+ const transport = new StdioServerTransport();
17
+ await server.connect(transport);
18
+ }
19
+
20
+ main().catch((err) => {
21
+ console.error("Fatal:", err);
22
+ process.exit(1);
23
+ });
package/src/server.ts ADDED
@@ -0,0 +1,94 @@
1
+ /**
2
+ * MCP server — registers all tools from corp-tools definitions,
3
+ * dispatches to the backend API via CorpAPIClient.
4
+ */
5
+
6
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
7
+ import { z } from "zod";
8
+ import {
9
+ GENERATED_TOOL_DEFINITIONS,
10
+ CorpAPIClient,
11
+ executeTool,
12
+ type ToolContext,
13
+ } from "@thecorporation/corp-tools";
14
+ import { join } from "node:path";
15
+ import { homedir } from "node:os";
16
+
17
+ // ---------------------------------------------------------------------------
18
+ // Build Zod schemas from JSON Schema properties
19
+ // ---------------------------------------------------------------------------
20
+
21
+ interface ToolFunction {
22
+ name: string;
23
+ description: string;
24
+ parameters: {
25
+ type: string;
26
+ properties: Record<string, { type: string; description?: string; enum?: string[]; items?: { type: string } }>;
27
+ required: string[];
28
+ };
29
+ }
30
+
31
+ interface ToolDef {
32
+ type: string;
33
+ function: ToolFunction;
34
+ }
35
+
36
+ function buildZodShape(fn: ToolFunction): Record<string, z.ZodTypeAny> {
37
+ const shape: Record<string, z.ZodTypeAny> = {};
38
+ const props = fn.parameters.properties || {};
39
+ const required = new Set(fn.parameters.required || []);
40
+
41
+ for (const [pname, pinfo] of Object.entries(props)) {
42
+ let schema: z.ZodTypeAny;
43
+ switch (pinfo.type) {
44
+ case "integer":
45
+ schema = z.number().int();
46
+ break;
47
+ case "number":
48
+ schema = z.number();
49
+ break;
50
+ case "boolean":
51
+ schema = z.boolean();
52
+ break;
53
+ case "array":
54
+ schema = z.array(pinfo.items?.type === "object" ? z.record(z.unknown()) : z.string());
55
+ break;
56
+ case "object":
57
+ schema = z.record(z.unknown());
58
+ break;
59
+ default:
60
+ schema = pinfo.enum ? z.enum(pinfo.enum as [string, ...string[]]) : z.string();
61
+ }
62
+ if (pinfo.description) schema = schema.describe(pinfo.description);
63
+ if (!required.has(pname)) schema = schema.optional();
64
+ shape[pname] = schema;
65
+ }
66
+ return shape;
67
+ }
68
+
69
+ // ---------------------------------------------------------------------------
70
+ // Create and configure MCP server
71
+ // ---------------------------------------------------------------------------
72
+
73
+ export function createMcpServer(client: CorpAPIClient): McpServer {
74
+ const server = new McpServer({
75
+ name: "thecorporation",
76
+ version: "0.1.0",
77
+ });
78
+
79
+ const ctx: ToolContext = {
80
+ dataDir: join(homedir(), ".corp", "data"),
81
+ };
82
+
83
+ for (const td of GENERATED_TOOL_DEFINITIONS as unknown as ToolDef[]) {
84
+ const fn = td.function;
85
+ const shape = buildZodShape(fn);
86
+
87
+ server.tool(fn.name, fn.description, shape, async (args) => {
88
+ const result = await executeTool(fn.name, args as Record<string, unknown>, client, ctx);
89
+ return { content: [{ type: "text" as const, text: result }] };
90
+ });
91
+ }
92
+
93
+ return server;
94
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "outDir": "dist",
7
+ "rootDir": "src",
8
+ "declaration": true,
9
+ "sourceMap": true,
10
+ "esModuleInterop": true,
11
+ "forceConsistentCasingInFileNames": true,
12
+ "strict": true,
13
+ "skipLibCheck": true
14
+ },
15
+ "include": ["src"]
16
+ }
package/tsup.config.ts ADDED
@@ -0,0 +1,13 @@
1
+ import { defineConfig } from "tsup";
2
+
3
+ export default defineConfig({
4
+ entry: ["src/index.ts"],
5
+ format: ["esm"],
6
+ target: "node20",
7
+ outDir: "dist",
8
+ clean: true,
9
+ dts: true,
10
+ sourcemap: true,
11
+ splitting: false,
12
+ banner: { js: "#!/usr/bin/env node" },
13
+ });