@anyforge/mcp-server 2.0.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,125 @@
1
+ # AnyForge MCP Server
2
+
3
+ Drive AnyForge from **Claude CLI** (Claude Code), Cursor, and other MCP
4
+ clients: view and approve **HIL** notifications, read **programs /
5
+ initiatives / work items**, **add and modify** work items, see **crew
6
+ outputs**, and **interact with crews**.
7
+
8
+ The server is a thin client over AnyForge's key-authenticated control
9
+ API (`/api/v1/control/*`). It holds no business logic and never needs a
10
+ Firebase login.
11
+
12
+ > **User-facing setup guide:** [Drive AnyForge from Claude CLI](https://anyforge.ai/docs/mcp-server)
13
+ > (`apps/www/content/docs/mcp-server.md`). This README is the in-repo
14
+ > developer reference; the docs page is the canonical install walkthrough.
15
+
16
+ ## 1. Authorise this machine (once)
17
+
18
+ ```bash
19
+ npx @anyforge/control init
20
+ ```
21
+
22
+ This runs the device-auth flow and writes a long-lived `sk_anyforge_…`
23
+ key to `~/.anyforge/credentials`. The MCP server reads that file — so
24
+ you don't put any secret in your MCP config.
25
+
26
+ > The key carries scopes (`read` / `write` / `approve`). A read-only key
27
+ > can list and inspect but can't create work items or approve HILs.
28
+
29
+ ## 2. Register with Claude CLI
30
+
31
+ ```bash
32
+ claude mcp add anyforge -- npx tsx /ABS/PATH/TO/packages/control/mcp-server/index.ts
33
+ ```
34
+
35
+ - Add `--scope user` to make it available in every project, or
36
+ `--scope project` to commit it to this repo's `.mcp.json`.
37
+ - No env vars are required — credentials come from
38
+ `~/.anyforge/credentials`.
39
+
40
+ To point at a non-prod deployment or inline a key instead of using the
41
+ credentials file, pass env vars:
42
+
43
+ ```bash
44
+ claude mcp add anyforge \
45
+ -e ANYFORGE_API_URL=https://staging.anyforge.ai \
46
+ -e ANYFORGE_API_TOKEN=sk_anyforge_xxx \
47
+ -- npx tsx /ABS/PATH/TO/packages/control/mcp-server/index.ts
48
+ ```
49
+
50
+ ### Cursor / other clients
51
+
52
+ Use the equivalent stdio MCP config (`command: npx`, `args: ["tsx",
53
+ "…/index.ts"]`). The same credential resolution applies.
54
+
55
+ ## Tools
56
+
57
+ | Tool | Scope | Description |
58
+ |------|-------|-------------|
59
+ | `anyforge_list_pending_approvals` | read | List HIL approvals awaiting a decision |
60
+ | `anyforge_get_approval_detail` | read | Read one approval's plan / ADR / security context |
61
+ | `anyforge_submit_approval` | approve | Approve or reject a HIL (resumes the crew) |
62
+ | `anyforge_list_programs` | read | List programs |
63
+ | `anyforge_get_program` | read | Get a program |
64
+ | `anyforge_list_initiatives` | read | List initiatives with progress + spend |
65
+ | `anyforge_get_initiative` | read | Initiative + crews + burn-down |
66
+ | `anyforge_approve_backlog` | approve | Approve a proposed backlog (HIL #1) |
67
+ | `anyforge_approve_task_proposal` | approve | Approve a mid-flight task proposal |
68
+ | `anyforge_list_work_items` | read | List backlog items + crew state |
69
+ | `anyforge_get_work_item` | read | Get one work item |
70
+ | `anyforge_create_work_item` | write | Create a work item |
71
+ | `anyforge_update_work_item` | write | Edit / advance / dispatch / archive / close |
72
+ | `anyforge_get_crew` | read | Crew state + recent outputs + comments |
73
+ | `anyforge_post_crew_comment` | write | Comment on / @-mention in a crew |
74
+ | `anyforge_resume_crew` | approve | Resume a PAUSED/HALTED crew |
75
+ | `anyforge_halt_crew` | approve | Halt a crew |
76
+
77
+ ## Example prompts
78
+
79
+ - "What's waiting for my approval in AnyForge?" → `anyforge_list_pending_approvals`
80
+ - "Show me the plan for approval `abc123` then approve it." → `anyforge_get_approval_detail` → `anyforge_submit_approval`
81
+ - "Add a P1 bug: rate-limit the export endpoint." → `anyforge_create_work_item`
82
+ - "What did crew `xyz` produce? Post a comment asking it to add tests." → `anyforge_get_crew` → `anyforge_post_crew_comment`
83
+
84
+ ## How it works
85
+
86
+ ```
87
+ Claude CLI ──stdio──▶ MCP server ──HTTPS (Bearer sk_anyforge_…)──▶ /api/v1/control/* ──▶ shared control services
88
+ ```
89
+
90
+ The `/api/v1/control/*` routes share the exact same service layer as the
91
+ AnyForge web app, so the CLI and the dashboard behave identically.
92
+
93
+ ## Build & publish (maintainers)
94
+
95
+ The package ships **compiled JS** (`dist/`), so end users can run
96
+ `npx -y @anyforge/mcp-server` without a TypeScript toolchain. Source lives
97
+ in `index.ts` / `lib/` / `tools/`; `tsc` emits `dist/` (shebang preserved,
98
+ `.js` import specifiers already in source for NodeNext).
99
+
100
+ ```bash
101
+ npm install # includes the typescript devDependency
102
+ npm run build # tsc → dist/
103
+ npm pack --dry-run # verify the tarball: dist/ + README + package.json only
104
+ ```
105
+
106
+ Publishing is automated. **Push a `mcp-server-v<version>` tag** and the
107
+ `.github/workflows/publish-mcp-server.yml` workflow builds and runs
108
+ `npm publish` (scoped public). Keep the tag version in sync with
109
+ `package.json`:
110
+
111
+ ```bash
112
+ cd packages/control/mcp-server
113
+ npm version patch # bumps package.json, e.g. 2.0.0 → 2.0.1
114
+ git commit -am "chore(mcp): release mcp-server vX.Y.Z" # if not committed by npm version
115
+ git tag mcp-server-v$(node -p "require('./package.json').version")
116
+ git push origin main --tags
117
+ ```
118
+
119
+ > The workflow needs an `NPM_TOKEN` repo secret (an npm **automation**
120
+ > token for an account with publish rights on the `@anyforge` scope). One-
121
+ > time setup: `npm token create --read-only=false`, then add it under
122
+ > GitHub → Settings → Secrets → Actions as `NPM_TOKEN`.
123
+ >
124
+ > To publish manually instead: `npm login && npm publish` from this
125
+ > directory (`prepublishOnly` rebuilds `dist/` first).
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * AnyForge MCP Server
4
+ *
5
+ * Exposes AnyForge platform control as MCP tools so Claude CLI (Claude
6
+ * Code), Cursor, and other MCP clients can drive the platform: view and
7
+ * approve HIL notifications, read programs / initiatives / work items,
8
+ * add and modify work items, see crew outputs, and interact with crews.
9
+ *
10
+ * This process holds ZERO business logic — every tool is a thin call to
11
+ * the key-authenticated control API (/api/v1/control/*). Auth is the
12
+ * long-lived `sk_anyforge_` key from `~/.anyforge/credentials` (written
13
+ * by `npx @anyforge/control init`) or the ANYFORGE_API_TOKEN env var.
14
+ *
15
+ * Register with Claude CLI:
16
+ * claude mcp add anyforge -- npx tsx <abs path>/index.ts
17
+ *
18
+ * See README.md for full setup.
19
+ */
20
+ export {};
package/dist/index.js ADDED
@@ -0,0 +1,65 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * AnyForge MCP Server
4
+ *
5
+ * Exposes AnyForge platform control as MCP tools so Claude CLI (Claude
6
+ * Code), Cursor, and other MCP clients can drive the platform: view and
7
+ * approve HIL notifications, read programs / initiatives / work items,
8
+ * add and modify work items, see crew outputs, and interact with crews.
9
+ *
10
+ * This process holds ZERO business logic — every tool is a thin call to
11
+ * the key-authenticated control API (/api/v1/control/*). Auth is the
12
+ * long-lived `sk_anyforge_` key from `~/.anyforge/credentials` (written
13
+ * by `npx @anyforge/control init`) or the ANYFORGE_API_TOKEN env var.
14
+ *
15
+ * Register with Claude CLI:
16
+ * claude mcp add anyforge -- npx tsx <abs path>/index.ts
17
+ *
18
+ * See README.md for full setup.
19
+ */
20
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
21
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
22
+ import { CallToolRequestSchema, ListToolsRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
23
+ import { hilTools } from './tools/hil.js';
24
+ import { workTools } from './tools/work.js';
25
+ import { initiativeTools } from './tools/initiatives.js';
26
+ import { programTools } from './tools/programs.js';
27
+ import { crewTools } from './tools/crews.js';
28
+ // ─── Assemble the tool registry ──────────────────────────────────────
29
+ const MODULES = [hilTools, workTools, initiativeTools, programTools, crewTools];
30
+ const TOOLS = MODULES.flatMap(m => m.tools);
31
+ const HANDLERS = Object.assign({}, ...MODULES.map(m => m.handlers));
32
+ // ─── Server setup ────────────────────────────────────────────────────
33
+ const server = new Server({ name: 'anyforge', version: '2.0.0' }, { capabilities: { tools: {} } });
34
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
35
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
36
+ const { name, arguments: args } = request.params;
37
+ const handler = HANDLERS[name];
38
+ if (!handler) {
39
+ return {
40
+ content: [{ type: 'text', text: `Error: unknown tool "${name}"` }],
41
+ isError: true,
42
+ };
43
+ }
44
+ try {
45
+ const result = await handler((args ?? {}));
46
+ return { content: [{ type: 'text', text: JSON.stringify(result, null, 2) }] };
47
+ }
48
+ catch (err) {
49
+ return {
50
+ content: [{ type: 'text', text: `Error: ${err instanceof Error ? err.message : String(err)}` }],
51
+ isError: true,
52
+ };
53
+ }
54
+ });
55
+ // ─── Start ───────────────────────────────────────────────────────────
56
+ async function main() {
57
+ const transport = new StdioServerTransport();
58
+ await server.connect(transport);
59
+ console.error(`AnyForge MCP Server started (stdio, ${TOOLS.length} tools)`);
60
+ }
61
+ main().catch(err => {
62
+ console.error('MCP Server failed to start:', err);
63
+ process.exit(1);
64
+ });
65
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Thin typed client over the AnyForge control API (/api/v1/control/*).
3
+ *
4
+ * The MCP server holds zero business logic — every tool handler calls
5
+ * callControl() and forwards the JSON. Auth is the long-lived
6
+ * sk_anyforge_ key resolved once and cached for the process lifetime.
7
+ *
8
+ * The control API speaks a `{ ok, data | error }` envelope. We unwrap
9
+ * it: on `ok` return `data`; otherwise throw a ControlApiError carrying
10
+ * the stable error code so the MCP layer surfaces an actionable message
11
+ * to Claude instead of a raw HTTP failure.
12
+ */
13
+ export declare class ControlApiError extends Error {
14
+ readonly code: string;
15
+ readonly status: number;
16
+ constructor(code: string, status: number, detail?: string);
17
+ }
18
+ export declare function callControl<T = unknown>(method: 'GET' | 'POST' | 'PATCH' | 'DELETE', path: string, body?: unknown): Promise<T>;
19
+ /** Build a `?a=b&c=d` query string from defined values only. */
20
+ export declare function qs(params: Record<string, string | number | boolean | undefined>): string;
@@ -0,0 +1,64 @@
1
+ import { resolveCredentials } from './credentials.js';
2
+ /**
3
+ * Thin typed client over the AnyForge control API (/api/v1/control/*).
4
+ *
5
+ * The MCP server holds zero business logic — every tool handler calls
6
+ * callControl() and forwards the JSON. Auth is the long-lived
7
+ * sk_anyforge_ key resolved once and cached for the process lifetime.
8
+ *
9
+ * The control API speaks a `{ ok, data | error }` envelope. We unwrap
10
+ * it: on `ok` return `data`; otherwise throw a ControlApiError carrying
11
+ * the stable error code so the MCP layer surfaces an actionable message
12
+ * to Claude instead of a raw HTTP failure.
13
+ */
14
+ export class ControlApiError extends Error {
15
+ code;
16
+ status;
17
+ constructor(code, status, detail) {
18
+ super(detail ? `${code}: ${detail}` : code);
19
+ this.name = 'ControlApiError';
20
+ this.code = code;
21
+ this.status = status;
22
+ }
23
+ }
24
+ let cached = null;
25
+ async function creds() {
26
+ if (!cached)
27
+ cached = await resolveCredentials();
28
+ return cached;
29
+ }
30
+ export async function callControl(method, path, body) {
31
+ const { apiKey, endpoint } = await creds();
32
+ const url = `${endpoint.replace(/\/$/, '')}/api/v1/control${path}`;
33
+ const res = await fetch(url, {
34
+ method,
35
+ headers: {
36
+ 'Content-Type': 'application/json',
37
+ Authorization: `Bearer ${apiKey}`,
38
+ },
39
+ ...(body !== undefined ? { body: JSON.stringify(body) } : {}),
40
+ });
41
+ let json;
42
+ try {
43
+ json = await res.json();
44
+ }
45
+ catch {
46
+ throw new ControlApiError('invalid_response', res.status, `non-JSON response from ${url}`);
47
+ }
48
+ if (!res.ok || json.ok === false) {
49
+ const code = typeof json.error === 'string' ? json.error : `http_${res.status}`;
50
+ const detail = typeof json.message === 'string' ? json.message
51
+ : typeof json.detail === 'string' ? json.detail
52
+ : undefined;
53
+ throw new ControlApiError(code, res.status, detail);
54
+ }
55
+ return json.data;
56
+ }
57
+ /** Build a `?a=b&c=d` query string from defined values only. */
58
+ export function qs(params) {
59
+ const pairs = Object.entries(params)
60
+ .filter(([, v]) => v !== undefined && v !== '')
61
+ .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(String(v))}`);
62
+ return pairs.length ? `?${pairs.join('&')}` : '';
63
+ }
64
+ //# sourceMappingURL=apiClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"apiClient.js","sourceRoot":"","sources":["../../lib/apiClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Resolve the AnyForge control credentials the MCP server uses to talk
3
+ * to /api/v1/control/*.
4
+ *
5
+ * Precedence:
6
+ * 1. Env vars (ANYFORGE_API_TOKEN + ANYFORGE_API_URL) — lets an MCP
7
+ * config inline a key explicitly if desired.
8
+ * 2. ~/.anyforge/credentials — the file written by
9
+ * `npx @anyforge/control init`. This is the default path, so a
10
+ * user who has already run init needs zero secrets in their MCP
11
+ * config: `claude mcp add anyforge -- npx tsx <path>/index.ts`.
12
+ *
13
+ * The key is a long-lived `sk_anyforge_…` token, so there's no
14
+ * expiry/refresh dance — unlike a Firebase ID token.
15
+ */
16
+ export interface ResolvedCredentials {
17
+ apiKey: string;
18
+ endpoint: string;
19
+ }
20
+ export declare function resolveCredentials(): Promise<ResolvedCredentials>;
@@ -0,0 +1,33 @@
1
+ import { promises as fs } from 'fs';
2
+ import { homedir } from 'os';
3
+ import { join } from 'path';
4
+ const DEFAULT_ENDPOINT = 'https://anyforge.ai';
5
+ const FILE = join(homedir(), '.anyforge', 'credentials');
6
+ export async function resolveCredentials() {
7
+ const envKey = process.env.ANYFORGE_API_TOKEN;
8
+ const envUrl = process.env.ANYFORGE_API_URL;
9
+ if (envKey) {
10
+ return { apiKey: envKey, endpoint: envUrl || DEFAULT_ENDPOINT };
11
+ }
12
+ const fromFile = await readCredentialsFile();
13
+ if (fromFile?.apiKey) {
14
+ return {
15
+ apiKey: fromFile.apiKey,
16
+ endpoint: envUrl || fromFile.endpoint || DEFAULT_ENDPOINT,
17
+ };
18
+ }
19
+ throw new Error('No AnyForge credentials found. Run `npx @anyforge/control init` to authorise this machine, ' +
20
+ 'or set ANYFORGE_API_TOKEN (and optionally ANYFORGE_API_URL) in the MCP server env.');
21
+ }
22
+ async function readCredentialsFile() {
23
+ try {
24
+ const text = await fs.readFile(FILE, 'utf8');
25
+ return JSON.parse(text);
26
+ }
27
+ catch (err) {
28
+ if (err.code === 'ENOENT')
29
+ return null;
30
+ throw err;
31
+ }
32
+ }
33
+ //# sourceMappingURL=credentials.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"credentials.js","sourceRoot":"","sources":["../../lib/credentials.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,IAAI,EAAE,EAAE,MAAM,IAAI,CAAA;AACnC,OAAO,EAAE,OAAO,EAAE,MAAM,IAAI,CAAA;AAC5B,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAA;AA6B3B,MAAM,gBAAgB,GAAG,qBAAqB,CAAA;AAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,aAAa,CAAC,CAAA;AAExD,MAAM,CAAC,KAAK,UAAU,kBAAkB;IACtC,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,kBAAkB,CAAA;IAC7C,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAA;IAE3C,IAAI,MAAM,EAAE,CAAC;QACX,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,IAAI,gBAAgB,EAAE,CAAA;IACjE,CAAC;IAED,MAAM,QAAQ,GAAG,MAAM,mBAAmB,EAAE,CAAA;IAC5C,IAAI,QAAQ,EAAE,MAAM,EAAE,CAAC;QACrB,OAAO;YACL,MAAM,EAAE,QAAQ,CAAC,MAAM;YACvB,QAAQ,EAAE,MAAM,IAAI,QAAQ,CAAC,QAAQ,IAAI,gBAAgB;SAC1D,CAAA;IACH,CAAC;IAED,MAAM,IAAI,KAAK,CACb,6FAA6F;QAC7F,oFAAoF,CACrF,CAAA;AACH,CAAC;AAED,KAAK,UAAU,mBAAmB;IAChC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,MAAM,EAAE,CAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAA;QAC5C,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAoB,CAAA;IAC5C,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAK,GAA6B,CAAC,IAAI,KAAK,QAAQ;YAAE,OAAO,IAAI,CAAA;QACjE,MAAM,GAAG,CAAA;IACX,CAAC;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { ToolModule } from './types.js';
2
+ /**
3
+ * Crew tools — see crew outputs and interact (operator comments,
4
+ * resume, halt).
5
+ */
6
+ export declare const crewTools: ToolModule;
@@ -0,0 +1,66 @@
1
+ import { callControl, qs } from '../lib/apiClient.js';
2
+ /**
3
+ * Crew tools — see crew outputs and interact (operator comments,
4
+ * resume, halt).
5
+ */
6
+ export const crewTools = {
7
+ tools: [
8
+ {
9
+ name: 'anyforge_get_crew',
10
+ description: 'Get a crew with its recent chat messages (the crew output) and operator comments. ' +
11
+ 'Use this to see what a crew produced and where it stands.',
12
+ inputSchema: {
13
+ type: 'object',
14
+ properties: {
15
+ crewId: { type: 'string', description: 'The crew id.' },
16
+ messages: { type: 'number', description: 'How many recent chat/comment entries (default 50, max 200).' },
17
+ },
18
+ required: ['crewId'],
19
+ },
20
+ },
21
+ {
22
+ name: 'anyforge_post_crew_comment',
23
+ description: 'Post an operator comment on a crew (optionally @-mentioning teammates by uid). ' +
24
+ 'Requires `write` scope.',
25
+ inputSchema: {
26
+ type: 'object',
27
+ properties: {
28
+ crewId: { type: 'string', description: 'The crew id.' },
29
+ body: { type: 'string', description: 'Comment text (max 4000 chars).' },
30
+ messageId: { type: 'string', description: 'Optional crewChat message id to anchor the comment to.' },
31
+ mentionedUids: { type: 'array', items: { type: 'string' }, description: 'Optional uids to mention.' },
32
+ },
33
+ required: ['crewId', 'body'],
34
+ },
35
+ },
36
+ {
37
+ name: 'anyforge_resume_crew',
38
+ description: 'Resume a PAUSED or HALTED crew from its last checkpoint. Requires `approve` scope.',
39
+ inputSchema: {
40
+ type: 'object',
41
+ properties: { crewId: { type: 'string', description: 'The crew id.' } },
42
+ required: ['crewId'],
43
+ },
44
+ },
45
+ {
46
+ name: 'anyforge_halt_crew',
47
+ description: 'Halt a crew (terminal stop). Idempotent on already-finished crews. Requires `approve` scope.',
48
+ inputSchema: {
49
+ type: 'object',
50
+ properties: { crewId: { type: 'string', description: 'The crew id.' } },
51
+ required: ['crewId'],
52
+ },
53
+ },
54
+ ],
55
+ handlers: {
56
+ anyforge_get_crew: args => callControl('GET', `/crews/${encodeURIComponent(String(args.crewId))}${qs({ messages: args.messages })}`),
57
+ anyforge_post_crew_comment: args => callControl('POST', `/crews/${encodeURIComponent(String(args.crewId))}/comments`, {
58
+ body: args.body,
59
+ ...(args.messageId ? { messageId: args.messageId } : {}),
60
+ ...(Array.isArray(args.mentionedUids) ? { mentionedUids: args.mentionedUids } : {}),
61
+ }),
62
+ anyforge_resume_crew: args => callControl('POST', `/crews/${encodeURIComponent(String(args.crewId))}/resume`),
63
+ anyforge_halt_crew: args => callControl('POST', `/crews/${encodeURIComponent(String(args.crewId))}/halt`),
64
+ },
65
+ };
66
+ //# sourceMappingURL=crews.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crews.js","sourceRoot":"","sources":["../../tools/crews.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ import type { ToolModule } from './types.js';
2
+ /**
3
+ * HIL (human-in-the-loop) approval tools — the headline capability:
4
+ * list what's awaiting the operator, read its context, and approve /
5
+ * reject from the CLI.
6
+ */
7
+ export declare const hilTools: ToolModule;
@@ -0,0 +1,56 @@
1
+ import { callControl, qs } from '../lib/apiClient.js';
2
+ /**
3
+ * HIL (human-in-the-loop) approval tools — the headline capability:
4
+ * list what's awaiting the operator, read its context, and approve /
5
+ * reject from the CLI.
6
+ */
7
+ export const hilTools = {
8
+ tools: [
9
+ {
10
+ name: 'anyforge_list_pending_approvals',
11
+ description: 'List HIL approvals awaiting an operator decision for your company. Returns each ' +
12
+ 'approval id, the crew it belongs to, plan summary, and the architecture / security ' +
13
+ 'context. Use this to see what needs your sign-off.',
14
+ inputSchema: {
15
+ type: 'object',
16
+ properties: {
17
+ limit: { type: 'number', description: 'Max approvals to return (default 50, max 200).' },
18
+ },
19
+ },
20
+ },
21
+ {
22
+ name: 'anyforge_get_approval_detail',
23
+ description: 'Get the full detail of a single HIL approval by id (plan, ADR, security audit).',
24
+ inputSchema: {
25
+ type: 'object',
26
+ properties: {
27
+ approvalId: { type: 'string', description: 'The HIL approval id.' },
28
+ },
29
+ required: ['approvalId'],
30
+ },
31
+ },
32
+ {
33
+ name: 'anyforge_submit_approval',
34
+ description: 'Submit a decision on a HIL approval. APPROVED resumes the crew past the gate; ' +
35
+ 'REJECTED sends it back. Requires a key with the `approve` scope.',
36
+ inputSchema: {
37
+ type: 'object',
38
+ properties: {
39
+ approvalId: { type: 'string', description: 'The HIL approval id.' },
40
+ decision: { type: 'string', description: "'APPROVED' or 'REJECTED'." },
41
+ notes: { type: 'string', description: 'Optional reviewer notes (shown in the audit trail).' },
42
+ },
43
+ required: ['approvalId', 'decision'],
44
+ },
45
+ },
46
+ ],
47
+ handlers: {
48
+ anyforge_list_pending_approvals: args => callControl('GET', `/hil${qs({ limit: args.limit })}`),
49
+ anyforge_get_approval_detail: args => callControl('GET', `/hil/${encodeURIComponent(String(args.approvalId))}`),
50
+ anyforge_submit_approval: args => callControl('POST', `/hil/${encodeURIComponent(String(args.approvalId))}`, {
51
+ decision: args.decision,
52
+ ...(args.notes ? { notes: args.notes } : {}),
53
+ }),
54
+ },
55
+ };
56
+ //# sourceMappingURL=hil.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hil.js","sourceRoot":"","sources":["../../tools/hil.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,qBAAqB,CAAA;AAGrD;;;;GAIG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAe;IAClC,KAAK,EAAE;QACL;YACE,IAAI,EAAE,iCAAiC;YACvC,WAAW,EACT,kFAAkF;gBAClF,qFAAqF;gBACrF,oDAAoD;YACtD,WAAW,EAAE;gBACX,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,gDAAgD,EAAE;iBACzF;aACF;SACF;QACD;YACE,IAAI,EAAE,8BAA8B;YACpC,WAAW,EAAE,iFAAiF;YAC9F,WAAW,EAAE;gBACX,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,sBAAsB,EAAE;iBACpE;gBACD,QAAQ,EAAE,CAAC,YAAY,CAAC;aACzB;SACF;QACD;YACE,IAAI,EAAE,0BAA0B;YAChC,WAAW,EACT,gFAAgF;gBAChF,kEAAkE;YACpE,WAAW,EAAE;gBACX,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,sBAAsB,EAAE;oBACnE,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,2BAA2B,EAAE;oBACtE,KAAK,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,qDAAqD,EAAE;iBAC9F;gBACD,QAAQ,EAAE,CAAC,YAAY,EAAE,UAAU,CAAC;aACrC;SACF;KACF;IACD,QAAQ,EAAE;QACR,+BAA+B,EAAE,IAAI,CAAC,EAAE,CACtC,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAA2B,EAAE,CAAC,EAAE,CAAC;QAC9E,4BAA4B,EAAE,IAAI,CAAC,EAAE,CACnC,WAAW,CAAC,KAAK,EAAE,QAAQ,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC;QAC3E,wBAAwB,EAAE,IAAI,CAAC,EAAE,CAC/B,WAAW,CAAC,MAAM,EAAE,QAAQ,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC,EAAE,EAAE;YACzE,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;SAC7C,CAAC;KACL;CACF,CAAA"}
@@ -0,0 +1,7 @@
1
+ import type { ToolModule } from './types.js';
2
+ /**
3
+ * Initiative tools — read initiatives + their crews/spend, and approve
4
+ * the proposed backlog / mid-flight task proposals (HIL #1 and task
5
+ * proposals).
6
+ */
7
+ export declare const initiativeTools: ToolModule;
@@ -0,0 +1,64 @@
1
+ import { callControl, qs } from '../lib/apiClient.js';
2
+ /**
3
+ * Initiative tools — read initiatives + their crews/spend, and approve
4
+ * the proposed backlog / mid-flight task proposals (HIL #1 and task
5
+ * proposals).
6
+ */
7
+ export const initiativeTools = {
8
+ tools: [
9
+ {
10
+ name: 'anyforge_list_initiatives',
11
+ description: 'List initiatives with status, progress, token budget/spend. Optionally filter by a ' +
12
+ 'comma-separated status list (DRAFT, BACKLOG_PROPOSED, IN_PROGRESS, COMPLETE, ARCHIVED).',
13
+ inputSchema: {
14
+ type: 'object',
15
+ properties: {
16
+ status: { type: 'string', description: 'Comma-separated InitiativeStatus filter.' },
17
+ },
18
+ },
19
+ },
20
+ {
21
+ name: 'anyforge_get_initiative',
22
+ description: 'Get an initiative with its crews, lifetime spend, and daily burn-down series.',
23
+ inputSchema: {
24
+ type: 'object',
25
+ properties: { initiativeId: { type: 'string', description: 'The initiative id.' } },
26
+ required: ['initiativeId'],
27
+ },
28
+ },
29
+ {
30
+ name: 'anyforge_approve_backlog',
31
+ description: 'Approve the proposed backlog for an initiative (HIL #1). Promotes its BACKLOG items to ' +
32
+ 'READY and moves the initiative to IN_PROGRESS. Requires `approve` scope.',
33
+ inputSchema: {
34
+ type: 'object',
35
+ properties: { initiativeId: { type: 'string', description: 'The initiative id.' } },
36
+ required: ['initiativeId'],
37
+ },
38
+ },
39
+ {
40
+ name: 'anyforge_approve_task_proposal',
41
+ description: 'Approve a mid-flight task proposal card, materialising a READY work item. Requires ' +
42
+ '`approve` scope.',
43
+ inputSchema: {
44
+ type: 'object',
45
+ properties: {
46
+ initiativeId: { type: 'string', description: 'The initiative id.' },
47
+ crewId: { type: 'string', description: 'The crew whose chat holds the proposal card.' },
48
+ messageId: { type: 'string', description: 'The task_proposal chat message id.' },
49
+ },
50
+ required: ['initiativeId', 'crewId', 'messageId'],
51
+ },
52
+ },
53
+ ],
54
+ handlers: {
55
+ anyforge_list_initiatives: args => callControl('GET', `/initiatives${qs({ status: args.status })}`),
56
+ anyforge_get_initiative: args => callControl('GET', `/initiatives/${encodeURIComponent(String(args.initiativeId))}`),
57
+ anyforge_approve_backlog: args => callControl('POST', `/initiatives/${encodeURIComponent(String(args.initiativeId))}/approve-backlog`),
58
+ anyforge_approve_task_proposal: args => callControl('POST', `/initiatives/${encodeURIComponent(String(args.initiativeId))}/approve-task-proposal`, {
59
+ crewId: args.crewId,
60
+ messageId: args.messageId,
61
+ }),
62
+ },
63
+ };
64
+ //# sourceMappingURL=initiatives.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"initiatives.js","sourceRoot":"","sources":["../../tools/initiatives.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,qBAAqB,CAAA;AAGrD;;;;GAIG;AACH,MAAM,CAAC,MAAM,eAAe,GAAe;IACzC,KAAK,EAAE;QACL;YACE,IAAI,EAAE,2BAA2B;YACjC,WAAW,EACT,qFAAqF;gBACrF,yFAAyF;YAC3F,WAAW,EAAE;gBACX,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,0CAA0C,EAAE;iBACpF;aACF;SACF;QACD;YACE,IAAI,EAAE,yBAAyB;YAC/B,WAAW,EAAE,+EAA+E;YAC5F,WAAW,EAAE;gBACX,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,oBAAoB,EAAE,EAAE;gBACnF,QAAQ,EAAE,CAAC,cAAc,CAAC;aAC3B;SACF;QACD;YACE,IAAI,EAAE,0BAA0B;YAChC,WAAW,EACT,yFAAyF;gBACzF,0EAA0E;YAC5E,WAAW,EAAE;gBACX,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE,EAAE,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,oBAAoB,EAAE,EAAE;gBACnF,QAAQ,EAAE,CAAC,cAAc,CAAC;aAC3B;SACF;QACD;YACE,IAAI,EAAE,gCAAgC;YACtC,WAAW,EACT,qFAAqF;gBACrF,kBAAkB;YACpB,WAAW,EAAE;gBACX,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,YAAY,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,oBAAoB,EAAE;oBACnE,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,8CAA8C,EAAE;oBACvF,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,oCAAoC,EAAE;iBACjF;gBACD,QAAQ,EAAE,CAAC,cAAc,EAAE,QAAQ,EAAE,WAAW,CAAC;aAClD;SACF;KACF;IACD,QAAQ,EAAE;QACR,yBAAyB,EAAE,IAAI,CAAC,EAAE,CAChC,WAAW,CAAC,KAAK,EAAE,eAAe,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAA4B,EAAE,CAAC,EAAE,CAAC;QACxF,uBAAuB,EAAE,IAAI,CAAC,EAAE,CAC9B,WAAW,CAAC,KAAK,EAAE,gBAAgB,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,EAAE,CAAC;QACrF,wBAAwB,EAAE,IAAI,CAAC,EAAE,CAC/B,WAAW,CAAC,MAAM,EAAE,gBAAgB,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,kBAAkB,CAAC;QACtG,8BAA8B,EAAE,IAAI,CAAC,EAAE,CACrC,WAAW,CAAC,MAAM,EAAE,gBAAgB,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,wBAAwB,EAAE;YACzG,MAAM,EAAE,IAAI,CAAC,MAAM;YACnB,SAAS,EAAE,IAAI,CAAC,SAAS;SAC1B,CAAC;KACL;CACF,CAAA"}
@@ -0,0 +1,5 @@
1
+ import type { ToolModule } from './types.js';
2
+ /**
3
+ * Program tools — read the cross-initiative planning layer.
4
+ */
5
+ export declare const programTools: ToolModule;
@@ -0,0 +1,33 @@
1
+ import { callControl, qs } from '../lib/apiClient.js';
2
+ /**
3
+ * Program tools — read the cross-initiative planning layer.
4
+ */
5
+ export const programTools = {
6
+ tools: [
7
+ {
8
+ name: 'anyforge_list_programs',
9
+ description: 'List programs (the cross-initiative planning grouping) with status and owner. ' +
10
+ 'Optionally filter by comma-separated status (PLANNING, IN_PROGRESS, COMPLETE, CANCELLED).',
11
+ inputSchema: {
12
+ type: 'object',
13
+ properties: {
14
+ status: { type: 'string', description: 'Comma-separated ProgramStatus filter.' },
15
+ },
16
+ },
17
+ },
18
+ {
19
+ name: 'anyforge_get_program',
20
+ description: 'Get a single program by id.',
21
+ inputSchema: {
22
+ type: 'object',
23
+ properties: { programId: { type: 'string', description: 'The program id.' } },
24
+ required: ['programId'],
25
+ },
26
+ },
27
+ ],
28
+ handlers: {
29
+ anyforge_list_programs: args => callControl('GET', `/programs${qs({ status: args.status })}`),
30
+ anyforge_get_program: args => callControl('GET', `/programs/${encodeURIComponent(String(args.programId))}`),
31
+ },
32
+ };
33
+ //# sourceMappingURL=programs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"programs.js","sourceRoot":"","sources":["../../tools/programs.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,qBAAqB,CAAA;AAGrD;;GAEG;AACH,MAAM,CAAC,MAAM,YAAY,GAAe;IACtC,KAAK,EAAE;QACL;YACE,IAAI,EAAE,wBAAwB;YAC9B,WAAW,EACT,gFAAgF;gBAChF,2FAA2F;YAC7F,WAAW,EAAE;gBACX,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE;oBACV,MAAM,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,uCAAuC,EAAE;iBACjF;aACF;SACF;QACD;YACE,IAAI,EAAE,sBAAsB;YAC5B,WAAW,EAAE,6BAA6B;YAC1C,WAAW,EAAE;gBACX,IAAI,EAAE,QAAQ;gBACd,UAAU,EAAE,EAAE,SAAS,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,WAAW,EAAE,iBAAiB,EAAE,EAAE;gBAC7E,QAAQ,EAAE,CAAC,WAAW,CAAC;aACxB;SACF;KACF;IACD,QAAQ,EAAE;QACR,sBAAsB,EAAE,IAAI,CAAC,EAAE,CAC7B,WAAW,CAAC,KAAK,EAAE,YAAY,EAAE,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,MAA4B,EAAE,CAAC,EAAE,CAAC;QACrF,oBAAoB,EAAE,IAAI,CAAC,EAAE,CAC3B,WAAW,CAAC,KAAK,EAAE,aAAa,kBAAkB,CAAC,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,CAAC;KAChF;CACF,CAAA"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Shared shapes for the MCP tool modules. Each domain file exports a
3
+ * `tools` array (the MCP tool definitions) and a `handlers` map
4
+ * (name → async function returning JSON-serialisable data). index.ts
5
+ * merges them and JSON-stringifies handler results into MCP text
6
+ * content.
7
+ */
8
+ export interface ToolDef {
9
+ name: string;
10
+ description: string;
11
+ inputSchema: {
12
+ type: 'object';
13
+ properties: Record<string, unknown>;
14
+ required?: string[];
15
+ };
16
+ }
17
+ export type ToolHandler = (args: Record<string, unknown>) => Promise<unknown>;
18
+ export interface ToolModule {
19
+ tools: ToolDef[];
20
+ handlers: Record<string, ToolHandler>;
21
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Shared shapes for the MCP tool modules. Each domain file exports a
3
+ * `tools` array (the MCP tool definitions) and a `handlers` map
4
+ * (name → async function returning JSON-serialisable data). index.ts
5
+ * merges them and JSON-stringifies handler results into MCP text
6
+ * content.
7
+ */
8
+ export {};
9
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../tools/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
@@ -0,0 +1,5 @@
1
+ import type { ToolModule } from './types.js';
2
+ /**
3
+ * Work-item tools — read the backlog and add / modify items.
4
+ */
5
+ export declare const workTools: ToolModule;
@@ -0,0 +1,89 @@
1
+ import { callControl, qs } from '../lib/apiClient.js';
2
+ /**
3
+ * Work-item tools — read the backlog and add / modify items.
4
+ */
5
+ export const workTools = {
6
+ tools: [
7
+ {
8
+ name: 'anyforge_list_work_items',
9
+ description: 'List work items in the AnyForge backlog with status, priority, and any linked crew ' +
10
+ 'state. By default archived items are excluded.',
11
+ inputSchema: {
12
+ type: 'object',
13
+ properties: {
14
+ includeArchived: { type: 'boolean', description: 'Include archived items (default false).' },
15
+ },
16
+ },
17
+ },
18
+ {
19
+ name: 'anyforge_get_work_item',
20
+ description: 'Get a single work item by id.',
21
+ inputSchema: {
22
+ type: 'object',
23
+ properties: { itemId: { type: 'string', description: 'The work item id.' } },
24
+ required: ['itemId'],
25
+ },
26
+ },
27
+ {
28
+ name: 'anyforge_create_work_item',
29
+ description: 'Create a new work item in the backlog (status BACKLOG). Requires `write` scope.',
30
+ inputSchema: {
31
+ type: 'object',
32
+ properties: {
33
+ title: { type: 'string', description: 'Work item title.' },
34
+ description: { type: 'string', description: 'Detailed description.' },
35
+ type: { type: 'string', description: 'FEATURE, BUG, ENHANCEMENT, KTLO, TECH_DEBT, INCIDENT.' },
36
+ priority: { type: 'string', description: 'P0, P1, P2, P3.' },
37
+ acceptanceCriteria: { type: 'string', description: 'Optional acceptance criteria.' },
38
+ initiativeId: { type: 'string', description: 'Optional parent initiative id (allocates a {CODE}-NNN).' },
39
+ },
40
+ required: ['title', 'type', 'priority'],
41
+ },
42
+ },
43
+ {
44
+ name: 'anyforge_update_work_item',
45
+ description: 'Mutate a work item. `action` selects the operation: ' +
46
+ "'update' (edit title/description/type/priority/acceptanceCriteria/dependsOn), " +
47
+ "'advance' (BACKLOG→REFINED), 'approve_refinement' (REFINED→READY), " +
48
+ "'dispatch' (requires crewId), 'complete', 'archive', 'unarchive', 'reorder' (sortOrder), " +
49
+ "'close-as-fixed' (requires closedReason). Requires `write` scope.",
50
+ inputSchema: {
51
+ type: 'object',
52
+ properties: {
53
+ itemId: { type: 'string', description: 'The work item id.' },
54
+ action: { type: 'string', description: 'The mutation to apply (see description).' },
55
+ title: { type: 'string' },
56
+ description: { type: 'string' },
57
+ type: { type: 'string' },
58
+ priority: { type: 'string' },
59
+ acceptanceCriteria: { type: 'string' },
60
+ bumpKind: { type: 'string', description: 'breaking | feat | fix | none.' },
61
+ dependsOn: { type: 'array', items: { type: 'string' }, description: 'Work-item ids this depends on.' },
62
+ crewId: { type: 'string', description: "For action='dispatch'." },
63
+ crewType: { type: 'string' },
64
+ sortOrder: { type: 'number', description: "For action='reorder'." },
65
+ closedReason: { type: 'string', description: "For action='close-as-fixed'." },
66
+ closedAsFixedByWorkItemId: { type: 'string' },
67
+ },
68
+ required: ['itemId', 'action'],
69
+ },
70
+ },
71
+ ],
72
+ handlers: {
73
+ anyforge_list_work_items: args => callControl('GET', `/work${qs({ includeArchived: args.includeArchived })}`),
74
+ anyforge_get_work_item: args => callControl('GET', `/work/${encodeURIComponent(String(args.itemId))}`),
75
+ anyforge_create_work_item: args => callControl('POST', '/work', {
76
+ title: args.title,
77
+ description: args.description,
78
+ type: args.type,
79
+ priority: args.priority,
80
+ ...(args.acceptanceCriteria ? { acceptanceCriteria: args.acceptanceCriteria } : {}),
81
+ ...(args.initiativeId ? { initiativeId: args.initiativeId } : {}),
82
+ }),
83
+ anyforge_update_work_item: args => {
84
+ const { itemId, ...rest } = args;
85
+ return callControl('PATCH', `/work/${encodeURIComponent(String(itemId))}`, rest);
86
+ },
87
+ },
88
+ };
89
+ //# sourceMappingURL=work.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"work.js","sourceRoot":"","sources":["../../tools/work.ts"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "@anyforge/mcp-server",
3
+ "version": "2.0.0",
4
+ "description": "AnyForge MCP Server — drive AnyForge (HIL approvals, initiatives, work items, crews) from Claude CLI, Cursor, and other MCP clients",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "bin": {
8
+ "anyforge-mcp-server": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist",
12
+ "README.md"
13
+ ],
14
+ "engines": {
15
+ "node": ">=18"
16
+ },
17
+ "scripts": {
18
+ "build": "tsc",
19
+ "type-check": "tsc --noEmit",
20
+ "prepublishOnly": "npm run build",
21
+ "start": "npx tsx index.ts",
22
+ "dev": "npx tsx index.ts"
23
+ },
24
+ "publishConfig": {
25
+ "access": "public"
26
+ },
27
+ "keywords": [
28
+ "anyforge",
29
+ "mcp",
30
+ "model-context-protocol",
31
+ "claude",
32
+ "claude-code"
33
+ ],
34
+ "license": "UNLICENSED",
35
+ "dependencies": {
36
+ "@modelcontextprotocol/sdk": "^1.12.1"
37
+ },
38
+ "devDependencies": {
39
+ "typescript": "^5.7.3"
40
+ }
41
+ }