@reneturcios/decisionscope 0.0.0-stage → 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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Rene Turcios
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 CHANGED
@@ -1,3 +1,179 @@
1
- # Temporary Holding Version
1
+ # decisionscope
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ decisionscope gives a tool read-only access to a hosted decision
4
+ record: a system of record for what was actually decided. Before
5
+ acting, a tool can ask the record a natural-language question —
6
+ "Are we still shipping the new onboarding flow?" — and get back
7
+ the current authoritative answer, or an explicit hold when the
8
+ record has no confident answer. A polling caller can also drain
9
+ decisions whose state changed since a cursor. The record only
10
+ answers; it never takes actions in your systems.
11
+
12
+ decisionscope is a forwarding client. It runs locally as a CLI
13
+ and a stdio MCP server, and forwards each call to the hosted
14
+ endpoint as a JSON-RPC `tools/call` request. The endpoint is named
15
+ by `RITHMO_MCP_URL` and defaults to the hosted service at
16
+ `https://app.rithmo.ai/api/mcp`; a self-hosted deployment exposes
17
+ the same tools and contract at its own address.
18
+
19
+ ## Setup
20
+
21
+ Every request carries a per-organization service token as a
22
+ Bearer token. The token resolves to exactly one organization,
23
+ must carry the `mcp:read` scope, and every result is scoped to
24
+ that organization. There is no anonymous access.
25
+
26
+ ```bash
27
+ export RITHMO_MCP_TOKEN="<your service token>"
28
+ # Optional; only needed for a self-hosted endpoint:
29
+ export RITHMO_MCP_URL="https://app.rithmo.ai/api/mcp"
30
+ ```
31
+
32
+ Keep the token in a secret store, never in code. With no token
33
+ set, every call fails before any request is sent, with an error
34
+ naming `RITHMO_MCP_TOKEN`.
35
+
36
+ ## Install
37
+
38
+ ```bash
39
+ npx @reneturcios/decisionscope whoami
40
+ ```
41
+
42
+ Or run the container image:
43
+
44
+ ```bash
45
+ docker run -i ghcr.io/cloudcorprecords/decisionscope
46
+ ```
47
+
48
+ The container defaults to the MCP server (see below). Pass CLI
49
+ arguments to use it as a CLI instead, and pass the two environment
50
+ variables above with `-e` so the container can reach your record.
51
+
52
+ ## CLI
53
+
54
+ ```bash
55
+ decisionscope query_decision "Are we still shipping the new onboarding flow?"
56
+ decisionscope query_decision "What did we decide about GitHub integration?" --type decision
57
+ decisionscope query_decision "Who owns the pricing change?" --owner "Priya" --surface jira
58
+ decisionscope list_decision_changes
59
+ decisionscope list_decision_changes --since "0_" --limit 200
60
+ decisionscope whoami
61
+
62
+ decisionscope --version
63
+ ```
64
+
65
+ Results print as JSON. Exit codes: `0` on success, `1` on an
66
+ operation error (including a missing token), `2` for usage
67
+ errors. Arguments are validated before the token is checked, so a
68
+ malformed call is always a usage error.
69
+
70
+ ## MCP server
71
+
72
+ One bin serves both faces. The `mcp` subcommand starts a stdio
73
+ MCP server:
74
+
75
+ ```bash
76
+ npx -y @reneturcios/decisionscope mcp
77
+ ```
78
+
79
+ MCP client configuration:
80
+
81
+ ```json
82
+ {
83
+ "mcpServers": {
84
+ "decisionscope": {
85
+ "command": "npx",
86
+ "args": ["-y", "@reneturcios/decisionscope", "mcp"],
87
+ "env": {
88
+ "RITHMO_MCP_TOKEN": "<your service token>"
89
+ }
90
+ }
91
+ }
92
+ }
93
+ ```
94
+
95
+ Object results are returned as a JSON text block and as
96
+ `structuredContent`, so clients can read
97
+ `res.structuredContent` directly.
98
+
99
+ ### Tools
100
+
101
+ | Tool | Input | What it returns |
102
+ | --- | --- | --- |
103
+ | `query_decision` | `question` (required); optional `type` (`action_item` \| `follow_up` \| `decision`), `owner`, `area`, `surface` | A result discriminated on `result`: `found`, `found_record`, or `no_record` — see below. |
104
+ | `list_decision_changes` | optional `since` (opaque cursor), optional `limit` (integer 1–200, default 50) | `{ changes, cursor, has_more }` — the changes since the cursor, in `(changed_at, decision_id)` order. |
105
+ | `whoami` | (none) | `{ org_id, scopes }` for the configured token. |
106
+
107
+ ### Reading a `query_decision` result
108
+
109
+ The `result` field has three variants, and the distinction
110
+ matters:
111
+
112
+ - `found` — a live answer from the commitment ledger. The answer
113
+ text is at `answer`, alongside `status`, `lifecycle_state`,
114
+ `owner` and `source` (both discriminated states, never bare
115
+ nulls), `decided_at`, `due_at`, `rationale`, `conditions`,
116
+ `director`, `context`, a `supersedes` chain (newest-first,
117
+ cycle-guarded, capped at 12 hops), an optional `spine`, and a
118
+ `confidence` score. Apart from `confidence`, a `found` result
119
+ for the same decision is byte-identical on every call.
120
+ - `found_record` — a live answer from the subject record. This is
121
+ an answer, not a refusal. The answer text is at
122
+ `subject.currentAnswer`, **not** `answer` — and it may be
123
+ `null`, meaning the subject is on record but there is no answer
124
+ text to act on.
125
+ - `no_record` — no confident answer. The bare form is just
126
+ `{ "result": "no_record" }`; a typed form adds a `record`
127
+ explaining why the answer is held (`closed`, `no_record`, or
128
+ `not_observed` when the asked-about surface is one the record
129
+ does not observe). A `premise_check`, when present, always has
130
+ `safe_to_proceed: false`. Never proceed on a `no_record`.
131
+
132
+ Gate on a usable answer, not on the variant name:
133
+
134
+ ```js
135
+ const r = res.structuredContent;
136
+ const answer =
137
+ r.result === 'found' ? r.answer :
138
+ r.result === 'found_record' ? r.subject.currentAnswer :
139
+ null;
140
+ if (answer != null && String(answer).trim() !== '') { /* act */ }
141
+ else { /* hold — no answer to act on */ }
142
+ ```
143
+
144
+ One special case: a `no_record` whose `record.reason` starts
145
+ with `record_unavailable:` means the record itself could not be
146
+ read (a transient failure). That is not "we decided no" — hold
147
+ and retry.
148
+
149
+ ### Draining changes
150
+
151
+ `list_decision_changes` is built for a polling trigger:
152
+
153
+ - First call (no `since`): returns no changes and the current
154
+ head cursor. You start from now; history is not replayed. Pass
155
+ `since: "0_"` to backfill from the start instead.
156
+ - `has_more` means the page was full (`changes.length ===
157
+ limit`). It can be true on an exactly-full final page, so the
158
+ next call may legitimately return zero changes — that is how
159
+ the drain terminates, not an error.
160
+ - When a page returns no rows, the cursor is echoed back
161
+ unchanged, so re-polling with it is safe and idempotent.
162
+ - Each change carries the same record core as a `found` result,
163
+ plus `changed_at` — but never `supersedes`, `spine`, or
164
+ `confidence`. Its `status` is wider: reversals appear as
165
+ `dropped` or `superseded`, values a `found` result never
166
+ returns.
167
+
168
+ ## Development
169
+
170
+ ```bash
171
+ npm install
172
+ npm run build # tsc -> dist/
173
+ npm test # node test runner over test/*.test.ts, no network (fake fetch only)
174
+ npm run dev # run the CLI from source via tsx
175
+ ```
176
+
177
+ ## License
178
+
179
+ MIT — see `LICENSE`.
package/dist/cli.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,130 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * decisionscope CLI.
4
+ *
5
+ * decisionscope query_decision <question> [--type <t>] [--owner <o>] [--area <a>] [--surface <s>]
6
+ * decisionscope query_decision --question <question> [--type <t>] [--owner <o>] [--area <a>] [--surface <s>]
7
+ * decisionscope list_decision_changes [--since <cursor>] [--limit <n>]
8
+ * decisionscope whoami
9
+ * decisionscope mcp
10
+ * decisionscope --version
11
+ *
12
+ * Endpoint from RITHMO_MCP_URL (default https://app.rithmo.ai/api/mcp);
13
+ * service token from RITHMO_MCP_TOKEN, sent as a Bearer token.
14
+ * Argument validation runs before any credential check.
15
+ * Exit codes: 0 success, 1 operation error, 2 usage error.
16
+ */
17
+ import { buildForwardArguments, callDecisionscopeTool, formatPayload, TOKEN_ENV, URL_ENV, VERSION, } from "./decisionscope.js";
18
+ function usage() {
19
+ console.error(`Usage:
20
+ decisionscope query_decision <question> [--type <action_item|follow_up|decision>] [--owner <name>] [--area <hint>] [--surface <system>]
21
+ decisionscope list_decision_changes [--since <cursor>] [--limit <1-200>]
22
+ decisionscope whoami
23
+ decisionscope mcp
24
+ decisionscope --version
25
+
26
+ Endpoint: ${URL_ENV} (default https://app.rithmo.ai/api/mcp).
27
+ Token: ${TOKEN_ENV} (per-organization service token, scope mcp:read).`);
28
+ process.exit(2);
29
+ }
30
+ /** Parse "--flag value" pairs; returns null on a dangling flag. */
31
+ function flagMap(rest) {
32
+ const map = new Map();
33
+ for (let i = 0; i < rest.length; i += 2) {
34
+ const key = rest[i];
35
+ if (!key.startsWith("--") || i + 1 >= rest.length)
36
+ return null;
37
+ map.set(key, rest[i + 1]);
38
+ }
39
+ return map;
40
+ }
41
+ async function main() {
42
+ const [cmd, ...rest] = process.argv.slice(2);
43
+ if (cmd === "--version" || cmd === "-v") {
44
+ console.log(VERSION);
45
+ return;
46
+ }
47
+ if (cmd === "mcp") {
48
+ await import("./mcp.js");
49
+ return;
50
+ }
51
+ if (!cmd)
52
+ usage();
53
+ const known = ["query_decision", "list_decision_changes", "whoami"];
54
+ if (!known.includes(cmd))
55
+ usage();
56
+ // Build and validate arguments BEFORE any credential check:
57
+ // invalid arguments are a usage error (exit 2), never a token error.
58
+ let args = {};
59
+ try {
60
+ if (cmd === "query_decision") {
61
+ const positional = [];
62
+ const flagArgs = [];
63
+ for (let i = 0; i < rest.length; i++) {
64
+ if (rest[i].startsWith("--")) {
65
+ flagArgs.push(rest[i], rest[i + 1] ?? "");
66
+ i++;
67
+ }
68
+ else {
69
+ positional.push(rest[i]);
70
+ }
71
+ }
72
+ if (positional.length > 1)
73
+ usage();
74
+ const flags = flagMap(flagArgs);
75
+ if (!flags)
76
+ usage();
77
+ const allowed = new Set(["--question", "--type", "--owner", "--area", "--surface"]);
78
+ for (const key of flags.keys())
79
+ if (!allowed.has(key))
80
+ usage();
81
+ const question = flags.get("--question") ?? positional[0];
82
+ if (question !== undefined)
83
+ args.question = question;
84
+ if (flags.has("--type"))
85
+ args.type = flags.get("--type");
86
+ if (flags.has("--owner"))
87
+ args.owner = flags.get("--owner");
88
+ if (flags.has("--area"))
89
+ args.area = flags.get("--area");
90
+ if (flags.has("--surface"))
91
+ args.surface = flags.get("--surface");
92
+ }
93
+ else if (cmd === "list_decision_changes") {
94
+ const flags = flagMap(rest);
95
+ if (!flags)
96
+ usage();
97
+ const allowed = new Set(["--since", "--limit"]);
98
+ for (const key of flags.keys())
99
+ if (!allowed.has(key))
100
+ usage();
101
+ if (flags.has("--since"))
102
+ args.since = flags.get("--since");
103
+ if (flags.has("--limit")) {
104
+ const raw = flags.get("--limit");
105
+ if (!/^-?\d+$/.test(raw))
106
+ usage();
107
+ args.limit = Number(raw);
108
+ }
109
+ }
110
+ else {
111
+ if (rest.length)
112
+ usage();
113
+ }
114
+ args = buildForwardArguments(cmd, args);
115
+ }
116
+ catch {
117
+ usage();
118
+ }
119
+ const fetchFn = globalThis.fetch;
120
+ try {
121
+ const out = await callDecisionscopeTool({ fetchFn }, cmd, args);
122
+ console.log(formatPayload(out));
123
+ }
124
+ catch (err) {
125
+ console.error(`error: ${err instanceof Error ? err.message : String(err)}`);
126
+ process.exit(1);
127
+ }
128
+ }
129
+ void main();
130
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * decisionscope core: a forwarding client for a hosted, read-only
3
+ * decision record.
4
+ *
5
+ * The upstream server is a hosted MCP endpoint. Each tool call here
6
+ * becomes exactly one JSON-RPC `tools/call` POST to the endpoint
7
+ * named by RITHMO_MCP_URL (default https://app.rithmo.ai/api/mcp),
8
+ * carrying the per-organization service token from RITHMO_MCP_TOKEN
9
+ * as `Authorization: Bearer <token>`. The token must carry the
10
+ * `mcp:read` scope; every result is scoped to the token's
11
+ * organization. There is no anonymous access.
12
+ *
13
+ * Rules honoured here, from the documented contract:
14
+ * - Argument validation runs before the credential check, and the
15
+ * credential check runs before any request: a missing or blank
16
+ * token fails with an error naming RITHMO_MCP_TOKEN and no
17
+ * request is ever sent.
18
+ * - `query_decision` returns the upstream payload verbatim. Its
19
+ * `result` field discriminates "found" (answer at `answer`),
20
+ * "found_record" (answer at `subject.currentAnswer`, which may
21
+ * be null), and "no_record" (a held answer, never an error).
22
+ * Callers must gate on a usable answer, not on the variant name.
23
+ * - `list_decision_changes` validates `limit` as an integer in
24
+ * 1-200 and rejects out-of-range or non-integer values instead
25
+ * of clamping; the `since` cursor is opaque and passed through
26
+ * unchanged. When the caller omits `limit`, it is not sent, so
27
+ * the upstream default (50) applies; the same holds for `since`
28
+ * (omit on the first call to start from now; "0_" backfills).
29
+ * - `whoami` takes no arguments and returns `{ org_id, scopes }`.
30
+ *
31
+ * The upstream reply may be a plain JSON-RPC body or an SSE stream
32
+ * (`data:` lines); both are unwrapped. A result's
33
+ * `structuredContent` wins when present; otherwise a JSON text
34
+ * content block is parsed; otherwise the raw result is returned.
35
+ * HTTP failures and JSON-RPC errors are thrown as errors — the
36
+ * record's own `no_record` outcome is a result, not an error.
37
+ *
38
+ * All HTTP goes through an injected FetchFn so the request
39
+ * shaping is fully testable offline with fake responses.
40
+ */
41
+ import type { CallDeps, ListDecisionChangesArgs, QueryDecisionArgs } from "./types.js";
42
+ export declare const VERSION = "0.1.0";
43
+ export declare const TOKEN_ENV = "RITHMO_MCP_TOKEN";
44
+ export declare const URL_ENV = "RITHMO_MCP_URL";
45
+ export declare const DEFAULT_BASE_URL = "https://app.rithmo.ai/api/mcp";
46
+ export declare const REQUIRED_SCOPE = "mcp:read";
47
+ export declare const TOOL_NAMES: readonly ["query_decision", "list_decision_changes", "whoami"];
48
+ export type ToolName = (typeof TOOL_NAMES)[number];
49
+ /** Read the service token out of an environment map. */
50
+ export declare function resolveToken(env?: Record<string, string | undefined>): string | undefined;
51
+ /** Resolve the endpoint: explicit value, then RITHMO_MCP_URL, then the hosted default. */
52
+ export declare function resolveBaseUrl(explicit?: string, env?: Record<string, string | undefined>): string;
53
+ /** Validate query_decision arguments and shape the forwarded arguments. */
54
+ export declare function buildQueryDecisionArgs(args: Record<string, unknown>): QueryDecisionArgs;
55
+ /** Validate list_decision_changes arguments and shape the forwarded arguments. */
56
+ export declare function buildListDecisionChangesArgs(args: Record<string, unknown>): ListDecisionChangesArgs;
57
+ /** Validate whoami arguments (none are accepted) and shape the forwarded arguments. */
58
+ export declare function buildWhoamiArgs(args: Record<string, unknown>): Record<string, never>;
59
+ /** Validate one tool's arguments and return the arguments to forward. */
60
+ export declare function buildForwardArguments(tool: string, args: Record<string, unknown>): Record<string, unknown>;
61
+ /** Generic entry point used by the MCP server and the CLI. */
62
+ export declare function callDecisionscopeTool(deps: CallDeps, tool: string, args: Record<string, unknown>): Promise<unknown>;
63
+ /** Render a payload as the JSON text block MCP clients show. */
64
+ export declare function formatPayload(payload: unknown): string;
@@ -0,0 +1,206 @@
1
+ export const VERSION = "0.1.0";
2
+ export const TOKEN_ENV = "RITHMO_MCP_TOKEN";
3
+ export const URL_ENV = "RITHMO_MCP_URL";
4
+ export const DEFAULT_BASE_URL = "https://app.rithmo.ai/api/mcp";
5
+ export const REQUIRED_SCOPE = "mcp:read";
6
+ export const TOOL_NAMES = [
7
+ "query_decision",
8
+ "list_decision_changes",
9
+ "whoami",
10
+ ];
11
+ const DECISION_TYPES = ["action_item", "follow_up", "decision"];
12
+ /** Read the service token out of an environment map. */
13
+ export function resolveToken(env = process.env) {
14
+ const raw = typeof env[TOKEN_ENV] === "string" ? env[TOKEN_ENV].trim() : "";
15
+ return raw ? raw : undefined;
16
+ }
17
+ /** Resolve the endpoint: explicit value, then RITHMO_MCP_URL, then the hosted default. */
18
+ export function resolveBaseUrl(explicit, env = process.env) {
19
+ const raw = (explicit ?? env[URL_ENV] ?? DEFAULT_BASE_URL).trim();
20
+ return raw.replace(/\/+$/, "") || DEFAULT_BASE_URL;
21
+ }
22
+ function reqString(args, field) {
23
+ const v = args[field];
24
+ if (typeof v !== "string" || !v.trim()) {
25
+ throw new Error(`${field} must be a non-empty string`);
26
+ }
27
+ return v;
28
+ }
29
+ function optString(args, field) {
30
+ const v = args[field];
31
+ if (v === undefined)
32
+ return undefined;
33
+ if (typeof v !== "string" || !v.trim()) {
34
+ throw new Error(`${field} must be a non-empty string when provided`);
35
+ }
36
+ return v;
37
+ }
38
+ /** Validate query_decision arguments and shape the forwarded arguments. */
39
+ export function buildQueryDecisionArgs(args) {
40
+ const out = { question: reqString(args, "question") };
41
+ const type = args.type;
42
+ if (type !== undefined) {
43
+ if (typeof type !== "string" || !DECISION_TYPES.includes(type)) {
44
+ throw new Error(`type must be one of ${DECISION_TYPES.join(", ")} when provided`);
45
+ }
46
+ out.type = type;
47
+ }
48
+ const owner = optString(args, "owner");
49
+ if (owner !== undefined)
50
+ out.owner = owner;
51
+ const area = optString(args, "area");
52
+ if (area !== undefined)
53
+ out.area = area;
54
+ const surface = optString(args, "surface");
55
+ if (surface !== undefined)
56
+ out.surface = surface;
57
+ return out;
58
+ }
59
+ /** Validate list_decision_changes arguments and shape the forwarded arguments. */
60
+ export function buildListDecisionChangesArgs(args) {
61
+ const out = {};
62
+ const since = args.since;
63
+ if (since !== undefined) {
64
+ if (typeof since !== "string" || !since) {
65
+ throw new Error("since must be a non-empty string when provided");
66
+ }
67
+ out.since = since;
68
+ }
69
+ const limit = args.limit;
70
+ if (limit !== undefined) {
71
+ if (typeof limit !== "number" ||
72
+ !Number.isInteger(limit) ||
73
+ limit < 1 ||
74
+ limit > 200) {
75
+ throw new Error("limit must be an integer between 1 and 200");
76
+ }
77
+ out.limit = limit;
78
+ }
79
+ return out;
80
+ }
81
+ /** Validate whoami arguments (none are accepted) and shape the forwarded arguments. */
82
+ export function buildWhoamiArgs(args) {
83
+ if (Object.keys(args ?? {}).length > 0) {
84
+ throw new Error("whoami takes no arguments");
85
+ }
86
+ return {};
87
+ }
88
+ /** Validate one tool's arguments and return the arguments to forward. */
89
+ export function buildForwardArguments(tool, args) {
90
+ const a = args ?? {};
91
+ switch (tool) {
92
+ case "query_decision":
93
+ return { ...buildQueryDecisionArgs(a) };
94
+ case "list_decision_changes":
95
+ return { ...buildListDecisionChangesArgs(a) };
96
+ case "whoami":
97
+ return { ...buildWhoamiArgs(a) };
98
+ default:
99
+ throw new Error(`Unknown tool: ${tool}`);
100
+ }
101
+ }
102
+ function isRecord(value) {
103
+ return typeof value === "object" && value !== null && !Array.isArray(value);
104
+ }
105
+ /** Parse a response body that may be plain JSON or an SSE stream of JSON-RPC messages. */
106
+ function parseRpcBody(bodyText) {
107
+ const trimmed = bodyText.trim();
108
+ if (!trimmed)
109
+ throw new Error("upstream returned an empty response body");
110
+ try {
111
+ const parsed = JSON.parse(trimmed);
112
+ if (isRecord(parsed))
113
+ return parsed;
114
+ throw new Error("upstream returned a non-object JSON response");
115
+ }
116
+ catch (err) {
117
+ if (err instanceof Error && err.message.startsWith("upstream returned"))
118
+ throw err;
119
+ // Fall through to SSE parsing.
120
+ }
121
+ let last;
122
+ for (const line of trimmed.split(/\r?\n/)) {
123
+ const m = line.match(/^data:\s*(.*)$/);
124
+ if (!m || !m[1].trim())
125
+ continue;
126
+ try {
127
+ const parsed = JSON.parse(m[1]);
128
+ if (isRecord(parsed))
129
+ last = parsed;
130
+ }
131
+ catch {
132
+ // A non-JSON data line is not an RPC message; keep scanning.
133
+ }
134
+ }
135
+ if (last)
136
+ return last;
137
+ throw new Error("upstream response was neither JSON nor an SSE stream of JSON-RPC messages");
138
+ }
139
+ /** Extract the tool payload from a JSON-RPC response envelope. */
140
+ function unwrapResult(rpc, url) {
141
+ if (isRecord(rpc.error)) {
142
+ const message = typeof rpc.error.message === "string" ? rpc.error.message : "unknown upstream error";
143
+ throw new Error(`upstream tools/call to ${url} failed: ${message}`);
144
+ }
145
+ const result = rpc.result;
146
+ if (!isRecord(result))
147
+ return result;
148
+ if (result.structuredContent !== undefined)
149
+ return result.structuredContent;
150
+ if (Array.isArray(result.content)) {
151
+ for (const block of result.content) {
152
+ if (isRecord(block) && block.type === "text" && typeof block.text === "string") {
153
+ try {
154
+ return JSON.parse(block.text);
155
+ }
156
+ catch {
157
+ return block.text;
158
+ }
159
+ }
160
+ }
161
+ }
162
+ return result;
163
+ }
164
+ /** Generic entry point used by the MCP server and the CLI. */
165
+ export async function callDecisionscopeTool(deps, tool, args) {
166
+ // Argument validation first, before any credential check.
167
+ const forwardArgs = buildForwardArguments(tool, args ?? {});
168
+ const env = deps.env ?? process.env;
169
+ const token = (typeof deps.token === "string" ? deps.token.trim() : "") || resolveToken(env);
170
+ if (!token) {
171
+ throw new Error(`${TOKEN_ENV} is not set: a per-organization service token with the ${REQUIRED_SCOPE} scope is required (sent as a Bearer token). No request was sent.`);
172
+ }
173
+ const url = resolveBaseUrl(deps.baseUrl, env);
174
+ const body = JSON.stringify({
175
+ jsonrpc: "2.0",
176
+ id: 1,
177
+ method: "tools/call",
178
+ params: { name: tool, arguments: forwardArgs },
179
+ });
180
+ let response;
181
+ try {
182
+ response = await deps.fetchFn(url, {
183
+ method: "POST",
184
+ headers: {
185
+ "Content-Type": "application/json",
186
+ Accept: "application/json, text/event-stream",
187
+ Authorization: `Bearer ${token}`,
188
+ },
189
+ body,
190
+ });
191
+ }
192
+ catch (err) {
193
+ throw new Error(`request to ${url} failed: ${err instanceof Error ? err.message : String(err)}`);
194
+ }
195
+ const bodyText = await response.text();
196
+ if (!response.ok) {
197
+ const detail = bodyText.trim() ? `: ${bodyText.trim().slice(0, 200)}` : "";
198
+ throw new Error(`upstream ${url} returned HTTP ${response.status}${detail}`);
199
+ }
200
+ return unwrapResult(parseRpcBody(bodyText), url);
201
+ }
202
+ /** Render a payload as the JSON text block MCP clients show. */
203
+ export function formatPayload(payload) {
204
+ return typeof payload === "string" ? payload : JSON.stringify(payload, null, 2);
205
+ }
206
+ //# sourceMappingURL=decisionscope.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decisionscope.js","sourceRoot":"","sources":["../src/decisionscope.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export * from "./types.js";
2
+ export * from "./decisionscope.js";
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export * from "./types.js";
2
+ export * from "./decisionscope.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,YAAY,CAAC;AAC3B,cAAc,oBAAoB,CAAC"}
package/dist/mcp.d.ts ADDED
@@ -0,0 +1 @@
1
+ export {};
package/dist/mcp.js ADDED
@@ -0,0 +1,138 @@
1
+ /**
2
+ * decisionscope MCP server. Tools: query_decision,
3
+ * list_decision_changes, whoami.
4
+ *
5
+ * Why the low-level Server instead of McpServer: the high-level
6
+ * helper converts zod schemas to JSON Schema and injects keys that
7
+ * are not in this tool's public spec ("$schema" and
8
+ * "additionalProperties"), which breaks exact tool-list/schema
9
+ * parity with the spec. This file serves tools/list with the
10
+ * spec's exact input schemas as explicit literals, while zod still
11
+ * validates every tools/call argument set.
12
+ *
13
+ * This server is a forwarding client: every call is forwarded to
14
+ * the hosted endpoint named by RITHMO_MCP_URL (default
15
+ * https://app.rithmo.ai/api/mcp) with the RITHMO_MCP_TOKEN service
16
+ * token as a Bearer token. A missing token is an isError result
17
+ * naming RITHMO_MCP_TOKEN, returned before any request is sent.
18
+ * Object payloads are returned both as a JSON text block and as
19
+ * structuredContent, matching how the documented contract is
20
+ * consumed (`res.structuredContent`).
21
+ */
22
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
23
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
24
+ import { CallToolRequestSchema, ErrorCode, ListToolsRequestSchema, McpError, } from "@modelcontextprotocol/sdk/types.js";
25
+ import { z } from "zod";
26
+ import { callDecisionscopeTool, formatPayload, resolveToken, VERSION, } from "./decisionscope.js";
27
+ const QueryDecision = z.object({
28
+ question: z.string(),
29
+ type: z.enum(["action_item", "follow_up", "decision"]).optional(),
30
+ owner: z.string().optional(),
31
+ area: z.string().optional(),
32
+ surface: z.string().optional(),
33
+ });
34
+ const ListDecisionChanges = z.object({
35
+ since: z.string().optional(),
36
+ limit: z.number().int().min(1).max(200).optional(),
37
+ });
38
+ const Whoami = z.object({});
39
+ const TOOLS = [
40
+ {
41
+ name: "query_decision",
42
+ description: "Look up the current authoritative decision for a natural-language question. Returns a found, found_record, or no_record result.",
43
+ inputSchema: {
44
+ type: "object",
45
+ properties: {
46
+ question: { type: "string", description: "The natural-language question to look up a decision for." },
47
+ type: {
48
+ type: "string",
49
+ enum: ["action_item", "follow_up", "decision"],
50
+ description: "Restrict to records of this type: action item, follow-up, or decision.",
51
+ },
52
+ owner: { type: "string", description: "Restrict to records owned by this person." },
53
+ area: { type: "string", description: "Restrict to records in this area." },
54
+ surface: { type: "string", description: "Restrict to records from this surface." },
55
+ },
56
+ required: ["question"],
57
+ },
58
+ },
59
+ {
60
+ name: "list_decision_changes",
61
+ description: "List decisions whose state changed since an opaque cursor, for polling. Omit since to start from now; pass \"0_\" to backfill from the start.",
62
+ inputSchema: {
63
+ type: "object",
64
+ properties: {
65
+ since: { type: "string", description: "Opaque cursor from a previous call; omit to start from now, or pass \"0_\" to backfill from the start." },
66
+ limit: { type: "integer", minimum: 1, maximum: 200, default: 50, description: "Maximum number of changes to return." },
67
+ },
68
+ required: [],
69
+ },
70
+ },
71
+ {
72
+ name: "whoami",
73
+ description: "Return the organization and scopes for the configured service token: { org_id, scopes }.",
74
+ inputSchema: {
75
+ type: "object",
76
+ properties: {},
77
+ required: [],
78
+ },
79
+ },
80
+ ];
81
+ const SCHEMAS = {
82
+ query_decision: QueryDecision,
83
+ list_decision_changes: ListDecisionChanges,
84
+ whoami: Whoami,
85
+ };
86
+ function resultPayload(value) {
87
+ const base = {
88
+ content: [
89
+ {
90
+ type: "text",
91
+ text: typeof value === "string" ? value : formatPayload(value),
92
+ },
93
+ ],
94
+ };
95
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
96
+ return {
97
+ ...base,
98
+ structuredContent: value,
99
+ };
100
+ }
101
+ return base;
102
+ }
103
+ function errorResult(err) {
104
+ return {
105
+ content: [
106
+ { type: "text", text: err instanceof Error ? err.message : String(err) },
107
+ ],
108
+ isError: true,
109
+ };
110
+ }
111
+ const server = new Server({ name: "decisionscope", version: VERSION }, { capabilities: { tools: {} } });
112
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: TOOLS }));
113
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
114
+ const { name, arguments: args } = request.params;
115
+ const schema = SCHEMAS[name];
116
+ if (!schema)
117
+ throw new McpError(ErrorCode.MethodNotFound, `Unknown tool: ${name}`);
118
+ const fetchFn = globalThis.fetch;
119
+ try {
120
+ const parsed = schema.parse(args ?? {});
121
+ const out = await callDecisionscopeTool({
122
+ fetchFn,
123
+ token: resolveToken(process.env),
124
+ baseUrl: process.env.RITHMO_MCP_URL,
125
+ }, name, parsed);
126
+ return resultPayload(out);
127
+ }
128
+ catch (err) {
129
+ if (err instanceof McpError)
130
+ throw err;
131
+ return errorResult(err);
132
+ }
133
+ });
134
+ async function main() {
135
+ await server.connect(new StdioServerTransport());
136
+ }
137
+ void main();
138
+ //# sourceMappingURL=mcp.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp.js","sourceRoot":"","sources":["../src/mcp.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * decisionscope types.
3
+ *
4
+ * The three tools are thin, typed views over a hosted, read-only
5
+ * decision record. Argument shapes follow the documented Input
6
+ * tables exactly. Result payloads are passed through verbatim from
7
+ * the upstream server, so the result types describe the documented
8
+ * variants without narrowing away fields the record may carry.
9
+ */
10
+ /** Restrict a decision query to one record type. */
11
+ export type DecisionType = "action_item" | "follow_up" | "decision";
12
+ /** Arguments for query_decision. */
13
+ export interface QueryDecisionArgs {
14
+ question: string;
15
+ type?: DecisionType;
16
+ owner?: string;
17
+ area?: string;
18
+ surface?: string;
19
+ }
20
+ /** Arguments for list_decision_changes. */
21
+ export interface ListDecisionChangesArgs {
22
+ /** Opaque cursor from a prior call; omit to start from now, "0_" to backfill. */
23
+ since?: string;
24
+ /** Page size, integer 1-200; the upstream default is 50. */
25
+ limit?: number;
26
+ }
27
+ /** Owner as a discriminated state, never a bare null. */
28
+ export type DecisionOwner = {
29
+ state: "owned";
30
+ name: string;
31
+ user_id: string;
32
+ } | {
33
+ state: "unowned";
34
+ };
35
+ /** Source as a discriminated state, never a bare null. */
36
+ export type DecisionSource = {
37
+ state: "resolved";
38
+ system: string;
39
+ ref: string;
40
+ url: string;
41
+ } | {
42
+ state: "unresolved";
43
+ };
44
+ /**
45
+ * A query_decision result: a discriminated union on `result`
46
+ * ("found" | "found_record" | "no_record"). The payload is kept
47
+ * open because each variant carries its own documented fields and
48
+ * the record is returned verbatim.
49
+ */
50
+ export type QueryDecisionResult = {
51
+ result: string;
52
+ } & Record<string, unknown>;
53
+ /** One entry of a list_decision_changes page. */
54
+ export type DecisionChange = Record<string, unknown>;
55
+ /** A list_decision_changes page: changes plus the drain cursor. */
56
+ export interface ListDecisionChangesResult {
57
+ changes: DecisionChange[];
58
+ cursor: string;
59
+ has_more: boolean;
60
+ }
61
+ /** whoami result: the organization the token resolves to, and its scopes. */
62
+ export interface WhoamiResult {
63
+ org_id: string;
64
+ scopes: string[];
65
+ }
66
+ /** Minimal HTTP response surface so tests can inject fixtures. */
67
+ export interface HttpResponse {
68
+ ok: boolean;
69
+ status: number;
70
+ statusText?: string;
71
+ headers: {
72
+ get(name: string): string | null;
73
+ };
74
+ text(): Promise<string>;
75
+ }
76
+ export type FetchFn = (url: string, init?: {
77
+ method?: string;
78
+ headers?: Record<string, string>;
79
+ body?: string;
80
+ redirect?: string;
81
+ }) => Promise<HttpResponse>;
82
+ /** Dependencies for a tool call; fetch is injectable for tests. */
83
+ export interface CallDeps {
84
+ fetchFn: FetchFn;
85
+ /** Service token; falls back to RITHMO_MCP_TOKEN in `env`. */
86
+ token?: string;
87
+ /** Endpoint override; falls back to RITHMO_MCP_URL in `env`, then the default. */
88
+ baseUrl?: string;
89
+ /** Environment map used for token / URL resolution. Defaults to process.env. */
90
+ env?: Record<string, string | undefined>;
91
+ }
package/dist/types.js ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * decisionscope types.
3
+ *
4
+ * The three tools are thin, typed views over a hosted, read-only
5
+ * decision record. Argument shapes follow the documented Input
6
+ * tables exactly. Result payloads are passed through verbatim from
7
+ * the upstream server, so the result types describe the documented
8
+ * variants without narrowing away fields the record may carry.
9
+ */
10
+ export {};
11
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG"}
package/package.json CHANGED
@@ -1,6 +1 @@
1
- {
2
- "name": "@reneturcios/decisionscope",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
1
+ {"bin":{"decisionscope":"dist/cli.js"},"dependencies":{"@modelcontextprotocol/sdk":"^1","zod":"^4"},"description":"Read-only access to a hosted decision record: query the authoritative decision for a question, drain decision changes since a cursor, and verify your service token. One bin serves a small CLI and a stdio MCP server that forwards to the hosted endpoint.","devDependencies":{"@types/node":"^20","tsx":"^4","typescript":"^5"},"engines":{"node":">=20"},"files":["dist","server.json"],"homepage":"https://github.com/CloudCorpRecords/decisionscope#readme","license":"MIT","mcpName":"io.github.CloudCorpRecords/decisionscope","name":"@reneturcios/decisionscope","private":false,"publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/CloudCorpRecords/decisionscope.git"},"scripts":{"build":"tsc","dev":"tsx src/cli.ts","test":"tsx --test test/*.test.ts"},"type":"module","version":"0.1.0"}
package/server.json ADDED
@@ -0,0 +1 @@
1
+ {"$schema":"https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json","description":"Read-only decision record: query decisions, drain decision changes, verify token.","name":"io.github.CloudCorpRecords/decisionscope","packages":[{"identifier":"@reneturcios/decisionscope","packageArguments":[{"type":"positional","value":"mcp"}],"registryBaseUrl":"https://registry.npmjs.org","registryType":"npm","runtimeHint":"npx","transport":{"type":"stdio"},"version":"0.1.0"},{"identifier":"ghcr.io/cloudcorprecords/decisionscope:0.1.0","packageArguments":[{"type":"positional","value":"mcp"}],"registryType":"oci","runtimeArguments":[{"name":"--rm","type":"named"},{"name":"-i","type":"named"}],"runtimeHint":"docker","transport":{"type":"stdio"}}],"repository":{"source":"github","url":"https://github.com/CloudCorpRecords/decisionscope"},"version":"0.1.0"}