@process-cart/mcp 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +160 -0
  3. package/dist/bin.d.ts +2 -0
  4. package/dist/bin.js +21 -0
  5. package/dist/cli.d.ts +45 -0
  6. package/dist/cli.js +196 -0
  7. package/dist/index.d.ts +4 -0
  8. package/dist/index.js +6 -0
  9. package/dist/render.d.ts +139 -0
  10. package/dist/render.js +582 -0
  11. package/dist/server.d.ts +57 -0
  12. package/dist/server.js +261 -0
  13. package/dist/tools/add-comment.d.ts +6 -0
  14. package/dist/tools/add-comment.js +27 -0
  15. package/dist/tools/complete-task.d.ts +5 -0
  16. package/dist/tools/complete-task.js +21 -0
  17. package/dist/tools/decide-approval.d.ts +8 -0
  18. package/dist/tools/decide-approval.js +37 -0
  19. package/dist/tools/find-runs.d.ts +18 -0
  20. package/dist/tools/find-runs.js +74 -0
  21. package/dist/tools/get-activity.d.ts +10 -0
  22. package/dist/tools/get-activity.js +61 -0
  23. package/dist/tools/get-run.d.ts +3 -0
  24. package/dist/tools/get-run.js +16 -0
  25. package/dist/tools/get-workflow-schema.d.ts +3 -0
  26. package/dist/tools/get-workflow-schema.js +16 -0
  27. package/dist/tools/index.d.ts +3 -0
  28. package/dist/tools/index.js +33 -0
  29. package/dist/tools/list-people.d.ts +7 -0
  30. package/dist/tools/list-people.js +55 -0
  31. package/dist/tools/list-workflows.d.ts +9 -0
  32. package/dist/tools/list-workflows.js +70 -0
  33. package/dist/tools/reopen-task.d.ts +5 -0
  34. package/dist/tools/reopen-task.js +21 -0
  35. package/dist/tools/run-output.d.ts +6 -0
  36. package/dist/tools/run-output.js +13 -0
  37. package/dist/tools/set-fields.d.ts +5 -0
  38. package/dist/tools/set-fields.js +33 -0
  39. package/dist/tools/shared.d.ts +94 -0
  40. package/dist/tools/shared.js +97 -0
  41. package/dist/tools/start-run.d.ts +9 -0
  42. package/dist/tools/start-run.js +29 -0
  43. package/dist/tools/update-run.d.ts +10 -0
  44. package/dist/tools/update-run.js +31 -0
  45. package/dist/tools/whoami.d.ts +1 -0
  46. package/dist/tools/whoami.js +34 -0
  47. package/dist/version.d.ts +8 -0
  48. package/dist/version.js +8 -0
  49. package/package.json +58 -0
  50. package/server.json +39 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright © 2026 Process Cart
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,160 @@
1
+ # @process-cart/mcp
2
+
3
+ A [Model Context Protocol](https://modelcontextprotocol.io) server for
4
+ [Process Cart](https://developers.processcart.com). Add it to Claude Desktop, Claude Code, Cursor
5
+ or any other MCP client, give it an API key, and the assistant can find a workflow, start a run,
6
+ fill in its fields, complete its tasks, decide approvals, comment and read what happened.
7
+
8
+ It runs locally over stdio, calls the Process Cart API (`/api/v1`) with your key, and is built on
9
+ [`@process-cart/sdk`](https://www.npmjs.com/package/@process-cart/sdk). Full guide:
10
+ <https://developers.processcart.com/guides/mcp>.
11
+
12
+ ## Install
13
+
14
+ ```sh
15
+ npx -y @process-cart/mcp
16
+ ```
17
+
18
+ Your MCP client runs that command for you (see below). It needs Node.js 22.12 or later and a
19
+ Process Cart API key: an Admin creates one under **Settings → API**. A key acts on its whole
20
+ workspace, so give the assistant a key of its own with only the scopes it needs, and try it with
21
+ a sandbox (`pc_test_…`) key first.
22
+
23
+ ## Configure
24
+
25
+ ### Claude Desktop
26
+
27
+ **Settings → Developer → Edit Config**, then add to `claude_desktop_config.json` and restart:
28
+
29
+ ```json
30
+ {
31
+ "mcpServers": {
32
+ "processcart": {
33
+ "command": "npx",
34
+ "args": ["-y", "@process-cart/mcp"],
35
+ "env": { "PROCESSCART_API_KEY": "pc_live_…" }
36
+ }
37
+ }
38
+ }
39
+ ```
40
+
41
+ On Windows, if `npx` does not start, use `"command": "cmd"` with
42
+ `"args": ["/c", "npx", "-y", "@process-cart/mcp"]`.
43
+
44
+ ### Claude Code
45
+
46
+ ```sh
47
+ claude mcp add processcart -e PROCESSCART_API_KEY=pc_live_… -- npx -y @process-cart/mcp
48
+ ```
49
+
50
+ To share it through a project's checked-in `.mcp.json`, keep the key out of the file: Claude Code
51
+ expands `${VAR}` there, so register the variable (single quotes stop your shell expanding it) and
52
+ let each teammate export their own key:
53
+
54
+ ```sh
55
+ claude mcp add processcart --scope project -e 'PROCESSCART_API_KEY=${PROCESSCART_API_KEY}' -- npx -y @process-cart/mcp
56
+ ```
57
+
58
+ ### Cursor
59
+
60
+ The same `mcpServers` block as Claude Desktop, in `~/.cursor/mcp.json` (every project) or
61
+ `.cursor/mcp.json` (one project).
62
+
63
+ ## Environment and flags
64
+
65
+ | Setting | What it does |
66
+ | ----------------------- | -------------------------------------------------------------------------------------- |
67
+ | `PROCESSCART_API_KEY` | Your API key (`pc_live_…` or `pc_test_…`). Required. |
68
+ | `PROCESSCART_BASE_URL` | The API base URL. Default `https://app.processcart.com/api`. |
69
+ | `PROCESSCART_MCP_DEBUG` | `1` logs each tool call (name, outcome, duration) to stderr — never the key or values. |
70
+ | `--api-key <key>` | The key as a flag (prefer the environment: a command line is visible to others). |
71
+ | `--base-url <url>` | The base URL as a flag. |
72
+ | `--read-only` | Offer the read tools only, whatever the key allows. |
73
+ | `--help`, `--version` | Print the usage or the version and exit. |
74
+
75
+ The base URL must use `https`; plain `http` is accepted only for a server on this machine
76
+ (`localhost`, `127.x.x.x`, `::1`). Nothing but the MCP protocol is written to stdout; diagnostics go
77
+ to stderr, with anything shaped like a key cut to its prefix (`pc_live_…`). A missing or malformed
78
+ key, or an `http` URL to another host, exits with status 2 and a message on stderr.
79
+
80
+ ## Tools
81
+
82
+ | Tool | What it does | Scope |
83
+ | --------------------- | -------------------------------------------------------------------------------- | ------------------ |
84
+ | `whoami` | The workspace, the key, its scopes, the rate limit, the status labels, the tools | any key |
85
+ | `list_workflows` | Workflows the key can see; `q` searches names and descriptions | `workflows:read` |
86
+ | `get_workflow_schema` | A workflow's fields by key, with types, allowed values and the start form | `workflows:read` |
87
+ | `start_run` | Start a run with field values, priority, labels and a due date | `runs:write` |
88
+ | `find_runs` | Search runs by name, workflow, status, priority, labels, assignee, due window | `runs:read` |
89
+ | `get_run` | One run in full: tasks, fields, revision, app link | `runs:read` |
90
+ | `set_fields` | Set field values by key, all or nothing | `runs:write` |
91
+ | `complete_task` | Complete a task; a refusal names the required fields still blank | `runs:write` |
92
+ | `reopen_task` | Put a completed task back to to-do | `runs:write` |
93
+ | `decide_approval` | Approve or reject an approval task (a rejection needs a comment) | `approvals:decide` |
94
+ | `add_comment` | Comment on a task | `runs:write` |
95
+ | `get_activity` | A run's trail, newest first, with its latest comments | `runs:read` |
96
+ | `update_run` | Change a run's name, status, priority, labels or due date | `runs:write` |
97
+ | `list_people` | Members and groups, with the ids `find_runs` filters by | `users:read` |
98
+
99
+ Each tool answers Markdown for the model plus JSON as `structuredContent`. A run answer is the
100
+ whole run — tasks grouped by section with their keys, fields as "Label (`key`): value", and the
101
+ current `revision` — and its JSON is the API's Run resource, so no second call is needed to see
102
+ the result of a write. `find_runs` carries one summary per run (id, name, status, priority,
103
+ labels, dates, revision, workflow, link, task progress and the open tasks — no tasks or fields),
104
+ `list_people` carries `{ users, groups, complete }`, and `whoami` the API's `/v1/me` answer plus a
105
+ `server` member. `get_run` flags required fields that are still blank only when the key also has
106
+ `workflows:read` and the run is on the published version; otherwise `complete_task` names them.
107
+
108
+ At startup the server calls `GET /v1/me` (one attempt, at most 10 seconds) and registers only the
109
+ tools the key can use. A read-only key, or `--read-only`, gets the read tools only, and the
110
+ server's instructions then say it is read-only. If the key cannot be read at startup, the server
111
+ starts with the read tools only and `whoami` reports the error.
112
+
113
+ ## Safety
114
+
115
+ - No admin tools, and nothing deletes: no tool deletes a run, workflow or comment; `set_fields`
116
+ and `update_run` can overwrite or clear values; `reopen_task` and a rejection send work back.
117
+ Those four carry the MCP `destructiveHint`. Webhooks, schedules, automation rules, views,
118
+ workflow edits, files, CSV and Trash stay in the app and the REST API.
119
+ - Run names, field values, task titles, comments and activity are written by people, guests
120
+ included: the instructions and the read tools' descriptions tell the model to treat them as
121
+ data, never as instructions, and every such string is rendered on one line.
122
+ - A client's cancellation aborts the API request in flight.
123
+ - Every write carries an `Idempotency-Key`, so a retried request is applied once.
124
+ - The write tools take `expectedRevision` (sent as `Expected-Revision`): a write that raced
125
+ another change is refused with `409 revision_mismatch`, naming the current revision.
126
+ - API failures come back as tool results (`isError: true`) with the code, every field problem,
127
+ `currentRevision` on a 409 and the wait on a 429.
128
+ - The key is sent only to the API and never logged.
129
+
130
+ ## Programmatic use
131
+
132
+ ```ts
133
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
134
+ import { createServer } from "@process-cart/mcp";
135
+
136
+ const server = await createServer({ apiKey: process.env.PROCESSCART_API_KEY!, readOnly: true });
137
+ await server.connect(new StdioServerTransport());
138
+ ```
139
+
140
+ `createServer({ apiKey, baseUrl?, readOnly?, fetch?, log?, debug?, startupTimeoutMs? })` resolves
141
+ to an `McpServer` with the key's tools registered; connect it to any MCP transport.
142
+
143
+ ## Maintainers
144
+
145
+ Publish with **pnpm**, not npm: the manifest depends on `@process-cart/sdk` as `workspace:^`, which
146
+ `pnpm publish` rewrites to the SDK's real range (`^0.1.0`) in the tarball, and `npm publish` does
147
+ not.
148
+
149
+ ```sh
150
+ pnpm --filter @process-cart/sdk build
151
+ cd packages/mcp && pnpm publish --access public --no-git-checks
152
+ ```
153
+
154
+ `prepack` rebuilds `dist/`. `pnpm pack --dry-run` shows the tarball. Bump `version` in
155
+ `package.json`, `src/version.ts` and both versions in `server.json` together (`version.test.ts`
156
+ checks them). The MCP Registry listing (`server.json`, name `com.processcart/mcp`) is pending.
157
+
158
+ ## License
159
+
160
+ MIT
package/dist/bin.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export { parseArgs } from "./cli.js";
package/dist/bin.js ADDED
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env node
2
+ // `processcart-mcp`: the stdio entry point. Over stdio, stdout carries only
3
+ // the MCP transport, so any stray console output is routed to stderr before
4
+ // the server starts. The parser is re-exported for tests; importing this
5
+ // module does not start the server unless it is the script node runs.
6
+ import { isEntryPoint, main } from "./cli.js";
7
+ import { redactKeys } from "./server.js";
8
+ export { parseArgs } from "./cli.js";
9
+ if (isEntryPoint(import.meta.url, process.argv[1])) {
10
+ const toStderr = (...args) => console.error(...args);
11
+ console.log = toStderr;
12
+ console.info = toStderr;
13
+ console.debug = toStderr;
14
+ main().then((code) => {
15
+ if (code !== null)
16
+ process.exitCode = code;
17
+ }, (error) => {
18
+ process.stderr.write(`[processcart-mcp] ${redactKeys(error instanceof Error ? error.message : String(error))}\n`);
19
+ process.exitCode = 1;
20
+ });
21
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,45 @@
1
+ import type { Readable, Writable } from "node:stream";
2
+ export declare const HELP = "processcart-mcp 0.1.0 \u2014 the Process Cart MCP server (stdio)\n\nUsage:\n npx -y @process-cart/mcp [options]\n\nEnvironment:\n PROCESSCART_API_KEY A Process Cart API key (pc_live_\u2026 or pc_test_\u2026). Required.\n PROCESSCART_BASE_URL The API base URL (default https://app.processcart.com/api). https; plain\n http only for a server on this machine (localhost, 127.x.x.x, ::1).\n PROCESSCART_MCP_DEBUG Set to 1 to log each tool call (name, outcome, duration) to stderr.\n\nOptions:\n --api-key <key> The API key (prefer the environment variable: a flag is visible to\n other processes on this machine).\n --base-url <url> The API base URL.\n --read-only Offer the read tools only, whatever the key allows.\n -h, --help Show this help.\n -v, --version Show the version.\n\nDocs: https://developers.processcart.com/guides/mcp\n";
3
+ export interface ServeOptions {
4
+ apiKey: string;
5
+ baseUrl: string;
6
+ readOnly: boolean;
7
+ debug: boolean;
8
+ }
9
+ export type CliCommand = {
10
+ kind: "help";
11
+ } | {
12
+ kind: "version";
13
+ } | {
14
+ kind: "serve";
15
+ options: ServeOptions;
16
+ } | {
17
+ kind: "error";
18
+ message: string;
19
+ };
20
+ /** A loopback host (`localhost`, `127.0.0.0/8`, `::1`): the one place plain http may go. */
21
+ export declare function isLoopbackHost(hostname: string): boolean;
22
+ /**
23
+ * Parses the command line and environment. Flags win over the environment.
24
+ * Never echoes the key back in a message: an unknown option is named without
25
+ * its value, and a bare argument that looks like a key is not repeated.
26
+ */
27
+ export declare function parseArgs(argv: readonly string[], env: Readonly<Record<string, string | undefined>>): CliCommand;
28
+ /** The process surface `main` uses, injectable for tests. */
29
+ export interface CliIo {
30
+ argv: readonly string[];
31
+ env: Readonly<Record<string, string | undefined>>;
32
+ stdin: Readable;
33
+ stdout: Writable;
34
+ stderr: Pick<Writable, "write">;
35
+ /** Called once the client goes away (stdin ends) or a signal arrives. */
36
+ exit: (code: number) => void;
37
+ fetch?: typeof fetch;
38
+ }
39
+ /**
40
+ * Runs the CLI. Resolves to an exit code for --help, --version and usage
41
+ * errors (2), or to null once the server is serving on stdio.
42
+ */
43
+ export declare function main(io?: CliIo): Promise<number | null>;
44
+ /** True when `moduleUrl` is the script node was started with (symlinks and npx shims resolved). */
45
+ export declare function isEntryPoint(moduleUrl: string, argv1: string | undefined): boolean;
package/dist/cli.js ADDED
@@ -0,0 +1,196 @@
1
+ // The command line: `processcart-mcp` reads its key and options, builds the
2
+ // server and serves it over stdio. Over stdio, stdout IS the MCP transport —
3
+ // every diagnostic goes to stderr. Only --help and --version print to stdout,
4
+ // and they exit before any transport starts.
5
+ import { realpathSync } from "node:fs";
6
+ import path from "node:path";
7
+ import { fileURLToPath } from "node:url";
8
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
+ import { DEFAULT_BASE_URL } from "@process-cart/sdk";
10
+ import { createServer, redactKeys } from "./server.js";
11
+ import { MCP_VERSION } from "./version.js";
12
+ export const HELP = `processcart-mcp ${MCP_VERSION} — the Process Cart MCP server (stdio)
13
+
14
+ Usage:
15
+ npx -y @process-cart/mcp [options]
16
+
17
+ Environment:
18
+ PROCESSCART_API_KEY A Process Cart API key (pc_live_… or pc_test_…). Required.
19
+ PROCESSCART_BASE_URL The API base URL (default ${DEFAULT_BASE_URL}). https; plain
20
+ http only for a server on this machine (localhost, 127.x.x.x, ::1).
21
+ PROCESSCART_MCP_DEBUG Set to 1 to log each tool call (name, outcome, duration) to stderr.
22
+
23
+ Options:
24
+ --api-key <key> The API key (prefer the environment variable: a flag is visible to
25
+ other processes on this machine).
26
+ --base-url <url> The API base URL.
27
+ --read-only Offer the read tools only, whatever the key allows.
28
+ -h, --help Show this help.
29
+ -v, --version Show the version.
30
+
31
+ Docs: https://developers.processcart.com/guides/mcp
32
+ `;
33
+ const API_KEY_PATTERN = /^pc_(live|test)_\S+$/;
34
+ function truthy(value) {
35
+ return value !== undefined && /^(1|true|yes|on)$/i.test(value.trim());
36
+ }
37
+ /** A loopback host (`localhost`, `127.0.0.0/8`, `::1`): the one place plain http may go. */
38
+ export function isLoopbackHost(hostname) {
39
+ const host = hostname.toLowerCase();
40
+ return (host === "localhost" ||
41
+ host === "[::1]" ||
42
+ host === "::1" ||
43
+ /^127\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(host));
44
+ }
45
+ /**
46
+ * Parses the command line and environment. Flags win over the environment.
47
+ * Never echoes the key back in a message: an unknown option is named without
48
+ * its value, and a bare argument that looks like a key is not repeated.
49
+ */
50
+ export function parseArgs(argv, env) {
51
+ let apiKey;
52
+ let baseUrl;
53
+ let readOnly = false;
54
+ let version = false;
55
+ for (let i = 0; i < argv.length; i++) {
56
+ const arg = argv[i];
57
+ if (arg === "-h" || arg === "--help")
58
+ return { kind: "help" };
59
+ if (arg === "-v" || arg === "--version") {
60
+ version = true;
61
+ continue;
62
+ }
63
+ if (arg === "--read-only") {
64
+ readOnly = true;
65
+ continue;
66
+ }
67
+ const match = /^(--api-key|--base-url)(?:=(.*))?$/.exec(arg);
68
+ if (match) {
69
+ const value = match[2] ?? argv[i + 1];
70
+ if (match[2] === undefined)
71
+ i += 1;
72
+ if (value === undefined ||
73
+ value === "" ||
74
+ (match[2] === undefined && value.startsWith("--"))) {
75
+ return { kind: "error", message: `${match[1]} needs a value` };
76
+ }
77
+ if (match[1] === "--api-key")
78
+ apiKey = value;
79
+ else
80
+ baseUrl = value;
81
+ continue;
82
+ }
83
+ if (/pc_(live|test)_/.test(arg) && !arg.startsWith("-")) {
84
+ return {
85
+ kind: "error",
86
+ message: "an API key is not an argument: pass the key as --api-key or PROCESSCART_API_KEY (prefer the environment variable)",
87
+ };
88
+ }
89
+ const name = arg.split("=", 1)[0];
90
+ return { kind: "error", message: `unknown option ${JSON.stringify(redactKeys(name))}` };
91
+ }
92
+ if (version)
93
+ return { kind: "version" };
94
+ const key = (apiKey ?? env.PROCESSCART_API_KEY ?? "").trim();
95
+ if (key === "") {
96
+ return {
97
+ kind: "error",
98
+ message: "no API key: set PROCESSCART_API_KEY (an Admin creates keys under Settings → API in Process Cart)",
99
+ };
100
+ }
101
+ if (!API_KEY_PATTERN.test(key)) {
102
+ return {
103
+ kind: "error",
104
+ message: "the API key is not a Process Cart API key (they start with pc_live_ or pc_test_)",
105
+ };
106
+ }
107
+ const base = (baseUrl ?? env.PROCESSCART_BASE_URL ?? "").trim() || DEFAULT_BASE_URL;
108
+ let parsed;
109
+ try {
110
+ parsed = new URL(base);
111
+ }
112
+ catch {
113
+ return { kind: "error", message: `the base URL ${JSON.stringify(base)} is not a URL` };
114
+ }
115
+ if (parsed.protocol !== "https:" &&
116
+ !(parsed.protocol === "http:" && isLoopbackHost(parsed.hostname))) {
117
+ return {
118
+ kind: "error",
119
+ message: "the base URL must use https (http only for a server on this machine: localhost, 127.x.x.x or ::1), so the API key never travels in cleartext",
120
+ };
121
+ }
122
+ return {
123
+ kind: "serve",
124
+ options: { apiKey: key, baseUrl: base, readOnly, debug: truthy(env.PROCESSCART_MCP_DEBUG) },
125
+ };
126
+ }
127
+ function processIo() {
128
+ return {
129
+ argv: process.argv.slice(2),
130
+ env: process.env,
131
+ stdin: process.stdin,
132
+ stdout: process.stdout,
133
+ stderr: process.stderr,
134
+ exit: (code) => process.exit(code),
135
+ };
136
+ }
137
+ /**
138
+ * Runs the CLI. Resolves to an exit code for --help, --version and usage
139
+ * errors (2), or to null once the server is serving on stdio.
140
+ */
141
+ export async function main(io = processIo()) {
142
+ const command = parseArgs(io.argv, io.env);
143
+ if (command.kind === "help") {
144
+ io.stdout.write(HELP);
145
+ return 0;
146
+ }
147
+ if (command.kind === "version") {
148
+ io.stdout.write(`${MCP_VERSION}\n`);
149
+ return 0;
150
+ }
151
+ if (command.kind === "error") {
152
+ io.stderr.write(`processcart-mcp: ${redactKeys(command.message)}\nRun processcart-mcp --help for usage.\n`);
153
+ return 2;
154
+ }
155
+ const { options } = command;
156
+ // Every diagnostic is redacted: MCP clients keep server stderr in log files.
157
+ const log = (message) => void io.stderr.write(`[processcart-mcp] ${redactKeys(message)}\n`);
158
+ let server;
159
+ try {
160
+ server = await createServer({
161
+ apiKey: options.apiKey,
162
+ baseUrl: options.baseUrl,
163
+ readOnly: options.readOnly,
164
+ debug: options.debug,
165
+ log,
166
+ ...(io.fetch ? { fetch: io.fetch } : {}),
167
+ });
168
+ }
169
+ catch (error) {
170
+ log(`could not start: ${error instanceof Error ? error.message : String(error)}`);
171
+ return 2;
172
+ }
173
+ const transport = new StdioServerTransport(io.stdin, io.stdout);
174
+ let closing = false;
175
+ const shutdown = () => {
176
+ if (closing)
177
+ return;
178
+ closing = true;
179
+ void server.close().finally(() => io.exit(0));
180
+ };
181
+ io.stdin.once("end", shutdown);
182
+ io.stdin.once("close", shutdown);
183
+ await server.connect(transport);
184
+ return null;
185
+ }
186
+ /** True when `moduleUrl` is the script node was started with (symlinks and npx shims resolved). */
187
+ export function isEntryPoint(moduleUrl, argv1) {
188
+ if (!argv1)
189
+ return false;
190
+ try {
191
+ return realpathSync(fileURLToPath(moduleUrl)) === realpathSync(path.resolve(argv1));
192
+ }
193
+ catch {
194
+ return false;
195
+ }
196
+ }
@@ -0,0 +1,4 @@
1
+ export { buildInstructions, createServer, INSTRUCTIONS, problemOf, selectTools, STARTUP_TIMEOUT_MS, type CreateServerOptions, } from "./server.js";
2
+ export { TOOLS } from "./tools/index.js";
3
+ export type { ToolAccess, ToolContext, ToolDefinition, ToolOutput } from "./tools/index.js";
4
+ export { MCP_VERSION, SERVER_NAME } from "./version.js";
package/dist/index.js ADDED
@@ -0,0 +1,6 @@
1
+ // @process-cart/mcp — the programmatic entry point. Most people run the CLI
2
+ // (`npx -y @process-cart/mcp`); `createServer` builds the same server for a
3
+ // host that brings its own transport.
4
+ export { buildInstructions, createServer, INSTRUCTIONS, problemOf, selectTools, STARTUP_TIMEOUT_MS, } from "./server.js";
5
+ export { TOOLS } from "./tools/index.js";
6
+ export { MCP_VERSION, SERVER_NAME } from "./version.js";
@@ -0,0 +1,139 @@
1
+ import type { ActivityItem, Comment, GetWorkflowSchemaResponse, Group, Me, ProblemFieldError, Run, RunField, User, WorkflowSummary } from "@process-cart/sdk";
2
+ /** Caps that keep one answer readable. */
3
+ export declare const RENDER_LIMITS: {
4
+ /** Tasks listed in a run before "… and N more tasks". */
5
+ readonly tasks: 60;
6
+ /** Fields listed in a run (across its tasks) before "… and N more fields". */
7
+ readonly fields: 120;
8
+ /** Fields listed in a workflow schema. */
9
+ readonly schemaFields: 150;
10
+ /** Characters of one value, description or comment before it is cut. */
11
+ readonly value: 300;
12
+ };
13
+ /**
14
+ * Free text written by a person — a name, title, label, choice, value or
15
+ * comment — made safe to interpolate into one Markdown line: every line
16
+ * terminator becomes " / ", other control characters are dropped and runs of
17
+ * whitespace collapse, so the text can never start a line of its own (a
18
+ * heading, a list item, a line posing as an instruction). With `max`, it is
19
+ * also cut to that many characters.
20
+ */
21
+ export declare function text(value: string, max?: number): string;
22
+ /** Cuts `text` to `max` characters with an ellipsis. */
23
+ export declare function truncate(text: string, max?: number): string;
24
+ /** An ISO instant without its milliseconds (`2026-10-13T01:09:28Z`); a dash for null. */
25
+ export declare function instant(value: string | null | undefined): string;
26
+ /** The "pass this cursor" sentence every paged answer ends with. */
27
+ export declare function pagingLine(nextCursor: string | null | undefined, what: string): string;
28
+ /** A canonical value that counts as blank (what a required field may not be). */
29
+ export declare function isBlankValue(field: Pick<RunField, "type" | "value">): boolean;
30
+ export interface RenderRunOptions {
31
+ /**
32
+ * The field keys the run's workflow version marks required, when known
33
+ * (the server reads them once per version). Null or absent: no flags.
34
+ */
35
+ requiredKeys?: ReadonlySet<string> | null;
36
+ /** A sentence placed under the heading (what the tool just did). */
37
+ lead?: string;
38
+ }
39
+ /** Task counts over the visible tasks (what "progress" means in the app). */
40
+ export declare function runProgress(run: Pick<Run, "tasks">): {
41
+ done: number;
42
+ total: number;
43
+ };
44
+ /** The full run: header, then its tasks grouped by section with each task's fields beneath it. */
45
+ export declare function renderRun(run: Run, options?: RenderRunOptions): string;
46
+ /** A run without its tasks and fields: what find_runs returns per item. */
47
+ export interface RunSummary {
48
+ id: string;
49
+ name: string;
50
+ status: Run["status"];
51
+ priority: Run["priority"];
52
+ labels: string[];
53
+ dueAt: string | null;
54
+ keyDate: string | null;
55
+ revision: number;
56
+ updatedAt: string;
57
+ startedAt: string;
58
+ completedAt: string | null;
59
+ deletedAt: string | null;
60
+ workflow: Run["workflow"];
61
+ url: string;
62
+ progress: {
63
+ done: number;
64
+ total: number;
65
+ };
66
+ openTasks: {
67
+ key: string;
68
+ title: string;
69
+ }[];
70
+ }
71
+ export declare function summarizeRun(run: Run): RunSummary;
72
+ /** One page of runs, one entry each (get_run shows a run's tasks and fields). */
73
+ export declare function renderRunList(runs: readonly Run[], nextCursor: string | null): string;
74
+ export declare function renderWorkflowList(workflows: readonly WorkflowSummary[], nextCursor: string | null, options?: {
75
+ query?: string;
76
+ totalMatches?: number;
77
+ }): string;
78
+ /** One writable field as get_workflow_schema describes it. */
79
+ export interface SchemaFieldSummary {
80
+ key: string;
81
+ label: string;
82
+ type: string;
83
+ format: string | null;
84
+ taskKey: string | null;
85
+ sectionKey: string | null;
86
+ /** In the run-start (kickoff) form. */
87
+ kickoff: boolean;
88
+ /** Must be sent to start_run (a required kickoff field no rule can hide). */
89
+ requiredAtStart: boolean;
90
+ /** Required at start unless a visibility rule hides it. */
91
+ requiredUnlessHidden: boolean;
92
+ /** Allowed values (dropdown / multi-choice choices, subtask item keys). */
93
+ choices: string[];
94
+ /** Bounds and patterns, as short phrases. */
95
+ constraints: string[];
96
+ /** The field's help text and value notes, as the schema words them. */
97
+ notes: string | null;
98
+ }
99
+ /** Reads the API's per-workflow JSON Schemas into one summary per writable field, start fields first. */
100
+ export declare function summarizeSchema(schema: GetWorkflowSchemaResponse): SchemaFieldSummary[];
101
+ export declare function renderSchema(schema: GetWorkflowSchemaResponse): string;
102
+ export declare function renderComment(comment: Comment): string;
103
+ /** The comments get_activity shows: the last ones it read, how many it read, and whether that was all. */
104
+ export interface CommentWindow {
105
+ /** The newest comments read, oldest first. */
106
+ shown: readonly Comment[];
107
+ /** How many comments were read (every one of them when `complete`). */
108
+ total: number;
109
+ /** False when the scan stopped at its cap: the run has more comments than `total`. */
110
+ complete?: boolean;
111
+ }
112
+ export declare function renderActivity(items: readonly ActivityItem[], nextCursor: string | null, comments?: CommentWindow | null): string;
113
+ export declare function renderPeople(users: readonly User[], groups: readonly Group[], options?: {
114
+ query?: string;
115
+ truncated?: boolean;
116
+ }): string;
117
+ export interface ServerMode {
118
+ /** `--read-only` was passed. */
119
+ readOnlyFlag: boolean;
120
+ baseUrl: string;
121
+ /** The tools this server registered, by name. */
122
+ tools: readonly string[];
123
+ /** Why startup could not read the key (the tool list then holds read tools only). */
124
+ startupProblem: string | null;
125
+ }
126
+ export declare function renderMe(me: Me, mode: ServerMode): string;
127
+ /** The fields of a failure the renderer reads (a `ProcessCartError`, or any error mapped to this shape). */
128
+ export interface ProblemView {
129
+ status: number;
130
+ code: string;
131
+ title: string;
132
+ detail?: string;
133
+ requestId: string | null;
134
+ errors?: readonly ProblemFieldError[];
135
+ retryAfter?: number;
136
+ currentRevision?: number;
137
+ }
138
+ /** The error as a model reads it: what failed, every field problem, and what to do next. */
139
+ export declare function renderProblem(problem: ProblemView): string;