@tymio/mcp-server 1.0.0 → 1.0.1

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 CHANGED
@@ -1,84 +1,167 @@
1
- # Tymio MCP server (stdio)
1
+ # Tymio MCP CLI (`@tymio/mcp-server`)
2
2
 
3
- Local **stdio** MCP server for the Tymio hub: exposes REST APIs as [MCP](https://modelcontextprotocol.io/) tools using a **Bearer API key**. Use for scripts, CI, or when you do not want remote OAuth.
3
+ **Canonical Markdown for coding agents:** [`TYMIO_MCP_CLI_AGENT_GUIDANCE.md`](./TYMIO_MCP_CLI_AGENT_GUIDANCE.md) same text as `tymio-mcp instructions`, MCP server `instructions` (initialize), and `GET /api/mcp/agent-context` → `tymioMcpCliAgentGuidanceMarkdown` on the hub. It states explicitly that **there is no per-user MCP API key in Tymio Settings**; use OAuth (remote `/mcp` or `tymio-mcp login`).
4
4
 
5
- **Remote MCP** (recommended for daily Cursor use) runs inside the main Express app at `POST /mcp` with OAuth 2.1 and Google. See **[docs/HUB.md](../docs/HUB.md)** §6 for architecture, Google callback URL, and Cursor config (local + remote).
5
+ Installable **`tymio-mcp`** command: connect editors and agents to **Tymio** in two ways:
6
+
7
+ 1. **OAuth (default)** — stdio MCP server that **proxies** the hosted **Streamable HTTP** MCP endpoint (`…/mcp`) with the same **Google → Tymio** login as the web app. **Full tool surface** matches the hub (`server/src/mcp/tools.ts`).
8
+ 2. **API key (optional)** — if `DRD_API_KEY` or `API_KEY` is set, uses a **REST** bridge with a **fixed subset** of tools (see `mcp/src/apiKeyStdio.ts`).
6
9
 
7
10
  ---
8
11
 
9
- ## Prerequisites
12
+ ## Quick start (OAuth, production)
10
13
 
11
- 1. Server env: `API_KEY` set; optional `API_KEY_USER_ID` (otherwise first `SUPER_ADMIN` is used).
12
- 2. Tymio API running (e.g. `npm run dev` from repo root).
14
+ 1. Install the CLI (from npm when published, or `npm install -g /path/to/repo/mcp`).
15
+ 2. In a terminal:
13
16
 
14
- ## Environment
17
+ ```bash
18
+ tymio-mcp login
19
+ ```
15
20
 
16
- | Variable | Required | Description |
17
- |----------|----------|-------------|
18
- | `DRD_API_BASE_URL` | No (default `http://localhost:8080`) | Hub API base URL |
19
- | `DRD_API_KEY` | Yes for authenticated tools | Same value as server `API_KEY` |
21
+ A browser window opens; complete Google sign-in. Tokens and dynamic OAuth client data are stored under your user config directory (e.g. `~/.config/tymio-mcp` on Linux, or `~/Library/Application Support/tymio-mcp` on macOS).
20
22
 
21
- ## Install globally (npm)
23
+ 3. Point your MCP client at stdio **without** setting `DRD_API_KEY`:
22
24
 
23
- After the package is [published](https://docs.npmjs.com/creating-and-publishing-scoped-public-packages) to the `@tymio` scope (or from a local checkout):
25
+ ```json
26
+ {
27
+ "mcpServers": {
28
+ "tymio": {
29
+ "command": "tymio-mcp",
30
+ "args": []
31
+ }
32
+ }
33
+ }
34
+ ```
24
35
 
25
- ```bash
26
- npm install -g @tymio/mcp-server
27
- ```
36
+ 4. Optional: `tymio-mcp logout` removes saved OAuth files.
28
37
 
29
- This installs the **`tymio-mcp`** command on your `PATH`. Until it is on the registry, install from the repo:
38
+ **Agents / IDE:** MCP clients that support [server instructions](https://modelcontextprotocol.io) receive the same long-form guide as `tymio-mcp instructions` during the initialize handshake. You can still run `tymio-mcp instructions` in a terminal to print it, or read this README.
30
39
 
31
- ```bash
32
- npm install -g /absolute/path/to/proproman/mcp
33
- ```
40
+ ### OAuth callback port
34
41
 
35
- ## Build and run
42
+ The CLI listens on **`http://127.0.0.1:19876/callback`** during `login` (override with `TYMIO_OAUTH_PORT`). That URI must be reachable from your browser and should stay stable so it matches the dynamically registered OAuth client.
36
43
 
37
- ```bash
38
- npm run mcp:build
39
- npm run mcp:start
40
- ```
44
+ ### Hub URL
45
+
46
+ | Variable | Default | Purpose |
47
+ |----------|---------|---------|
48
+ | `TYMIO_MCP_URL` | `https://tymio.app/mcp` | Hosted MCP endpoint for OAuth proxy + `login` |
49
+
50
+ ---
51
+
52
+ ## API-key mode (REST subset, CI / automation)
41
53
 
42
- Or from `mcp/`: `npm run build` && `npm run start`. The process uses **stdio**; it is spawned by the MCP client, not run interactively.
54
+ If **`DRD_API_KEY` or `API_KEY`** is present in the environment, `tymio-mcp` **does not** use OAuth; it exposes the REST-based tool subset only.
43
55
 
44
- ## Cursor (stdio)
56
+ | Variable | Default | Purpose |
57
+ |----------|---------|---------|
58
+ | `DRD_API_BASE_URL` | `https://tymio.app` | Hub **origin** (no `/mcp`) |
59
+ | `DRD_API_KEY` / `API_KEY` | — | Bearer key (server `API_KEY`) |
45
60
 
46
- With a global install, point the client at the `tymio-mcp` binary:
61
+ Example:
47
62
 
48
63
  ```json
49
64
  {
50
65
  "mcpServers": {
51
- "tymio-local": {
66
+ "tymio-api-key": {
52
67
  "command": "tymio-mcp",
53
68
  "args": [],
54
69
  "env": {
55
- "DRD_API_BASE_URL": "http://localhost:8080",
56
- "DRD_API_KEY": "same-as-server-API_KEY"
70
+ "DRD_API_KEY": "your-key",
71
+ "DRD_API_BASE_URL": "https://tymio.app"
57
72
  }
58
73
  }
59
74
  }
60
75
  }
61
76
  ```
62
77
 
63
- Without a global install, use `node` and the built file:
78
+ ---
79
+
80
+ ## Commands
81
+
82
+ | Command | Description |
83
+ |---------|-------------|
84
+ | `tymio-mcp` | Run stdio MCP (OAuth proxy unless API key env is set) |
85
+ | `tymio-mcp login [url]` | OAuth sign-in; optional MCP URL overrides `TYMIO_MCP_URL` |
86
+ | `tymio-mcp logout` | Delete stored OAuth client + tokens |
87
+ | `tymio-mcp help` | Usage |
88
+
89
+ ---
90
+
91
+ ## Install globally (npm)
92
+
93
+ `npm install -g @tymio/mcp-server` works only **after** the package is published. **E404** means it is not on the registry yet. Publish from `mcp/`:
94
+
95
+ ```bash
96
+ cd mcp && npm login && npm publish --access public
97
+ ```
98
+
99
+ Or install from a clone:
100
+
101
+ ```bash
102
+ npm install -g /absolute/path/to/proproman/mcp
103
+ ```
104
+
105
+ ---
106
+
107
+ ## Build and run (monorepo)
108
+
109
+ ```bash
110
+ npm run mcp:build
111
+ npm run mcp:start
112
+ ```
113
+
114
+ From **`mcp/`**, run unit tests (uses `vitest.config.ts` in this folder):
115
+
116
+ ```bash
117
+ npm test
118
+ ```
119
+
120
+ From the **repo root**, use:
121
+
122
+ ```bash
123
+ npx vitest run --config mcp/vitest.config.ts
124
+ ```
125
+
126
+ Stdio processes are meant to be **spawned** by the MCP host, not run interactively.
127
+
128
+ ---
129
+
130
+ ## Direct remote MCP in Cursor (no CLI)
131
+
132
+ Your editor can use the hosted endpoint directly:
64
133
 
65
134
  ```json
66
135
  {
67
136
  "mcpServers": {
68
- "tymio-local": {
69
- "command": "node",
70
- "args": ["/ABSOLUTE/PATH/TO/repo/mcp/dist/index.js"],
71
- "env": {
72
- "DRD_API_BASE_URL": "http://localhost:8080",
73
- "DRD_API_KEY": "same-as-server-API_KEY"
74
- }
137
+ "tymio": {
138
+ "url": "https://tymio.app/mcp"
75
139
  }
76
140
  }
77
141
  }
78
142
  ```
79
143
 
80
- ## Tools
144
+ Use the **CLI** when the host only supports **stdio**, or you want a single npm-installed binary that reuses disk-persisted OAuth.
145
+
146
+ ---
147
+
148
+ ## Publishing to npm (maintainers)
149
+
150
+ The repo includes a **manual** GitHub Actions workflow (no automatic runs on push):
151
+
152
+ - **File:** `.github/workflows/mcp-server-publish.yml`
153
+ - **How to run:** GitHub → **Actions** → **MCP server — build & publish** → **Run workflow**
154
+ - **Default:** `dry-run` — runs `npm ci`, tests, build, `npm pack`, and `npm publish --dry-run` for the `mcp` workspace
155
+ - **Real publish:** choose input `publish` and ensure the repository secret **`NPM_TOKEN`** is set (npm automation token with publish access to the **`@tymio`** scope)
156
+
157
+ Local dry-run before tagging a release:
158
+
159
+ ```bash
160
+ npm run test --workspace mcp && npm run build --workspace mcp && npm publish -w mcp --access public --dry-run
161
+ ```
162
+
163
+ ---
81
164
 
82
- **Ontology / playbook (Tymio):** `tymio_get_coding_agent_guide` (full [CODING_AGENT_TYMIO.md](../docs/CODING_AGENT_TYMIO.md) from server), `tymio_get_agent_brief`, `tymio_list_capabilities`, `tymio_get_capability` — see [docs/HUB.md](../docs/HUB.md) §6.1.
165
+ ## Architecture reference
83
166
 
84
- **Backlog / data (historical `drd_*` prefix):** health, meta, initiatives, features, requirements, domains, products, accounts, partners, demands, campaigns, timeline, assignments, stakeholders, etc. Full list: `server/src/mcp/tools.ts` and `mcp/src/index.ts` (stdio subset).
167
+ Hosted MCP, OAuth, and Google callback URLs: **[docs/HUB.md](../docs/HUB.md)** §6. Hub tool implementations: `server/src/mcp/tools.ts`. OAuth proxy implementation: `mcp/src/hubProxyStdio.ts`. REST subset: `mcp/src/apiKeyStdio.ts`.
@@ -0,0 +1,99 @@
1
+ # Tymio MCP and `@tymio/mcp-server` — guidance for autonomous agents
2
+
3
+ ## Critical — read before advising the user
4
+
5
+ 1. **There is no per-user MCP API key in Tymio Settings, Profile, Account, or Integrations.** Do **not** instruct users to open the web app settings to “copy an API key for MCP” or “get the MCP key from preferences.” That path does not exist and causes repeated confusion.
6
+ 2. **Normal connection = OAuth**, not a personal secret from the UI:
7
+ - **Remote MCP in the IDE:** add server URL `https://tymio.app/mcp` (or your deployment origin + `/mcp`). Complete sign-in in the browser when the client prompts. **No API key to paste.**
8
+ - **Stdio npm package (default):** install `@tymio/mcp-server`, run `tymio-mcp login` once in a terminal (browser OAuth). Configure the MCP client to run `tymio-mcp` **without** setting `DRD_API_KEY` or `API_KEY` on that process unless you intentionally want API-key mode.
9
+ 3. **`DRD_API_KEY` / `API_KEY` on the stdio process** means the **server deployment automation secret** (the same value as the hub’s configured `API_KEY` for `Authorization: Bearer …` on REST). It is **not** something each user generates in the Tymio UI. Only operators with access to deployment secrets use it (CI, scripts, optional stdio “REST bridge” mode).
10
+ 4. **Default vs API-key stdio:** If `DRD_API_KEY` or `API_KEY` is set to a non-empty value in the **environment of the `tymio-mcp` process**, the CLI uses a **fixed REST tool subset**, not OAuth to the hosted hub. For the **full** tool surface, use **remote** `…/mcp` or stdio **without** those env vars (after `tymio-mcp login`).
11
+ 5. **Full text of this guide:** shell command `tymio-mcp instructions` (or `tymio-mcp guide`). MCP clients that support server `instructions` receive this content at initialize when using the published CLI.
12
+
13
+ ---
14
+
15
+ ## What `@tymio/mcp-server` is
16
+
17
+ - **Default (no API key env on the process):** A **stdio MCP server** that connects to the **hosted Tymio Streamable HTTP MCP** endpoint with **OAuth** (Google via the hub). It **proxies the full tool list** from the hub (same as using the remote URL in the IDE).
18
+ - **With `DRD_API_KEY` or `API_KEY` set:** A **REST/API-key** stdio server with a **smaller, fixed tool set** (good for CI/scripts).
19
+
20
+ ---
21
+
22
+ ## One-time setup (OAuth, stdio package)
23
+
24
+ 1. Install: `npm install -g @tymio/mcp-server` (or run a built `dist/index.js` via `node`).
25
+ 2. Run: `tymio-mcp login` — browser opens; complete Google sign-in for Tymio.
26
+ 3. Tokens and OAuth client metadata live under the user config directory (e.g. Linux: `~/.config/tymio-mcp`, macOS: `~/Library/Application Support/tymio-mcp`).
27
+ 4. **Callback:** default `http://127.0.0.1:19876/callback` during login — override with `TYMIO_OAUTH_PORT` if needed; keep stable for your registered OAuth client.
28
+
29
+ ---
30
+
31
+ ## Cursor / IDE (stdio, OAuth — recommended for this package)
32
+
33
+ Add an MCP server that runs the binary **without** `DRD_API_KEY` / `API_KEY`:
34
+
35
+ ```json
36
+ {
37
+ "mcpServers": {
38
+ "tymio": {
39
+ "command": "tymio-mcp",
40
+ "args": []
41
+ }
42
+ }
43
+ }
44
+ ```
45
+
46
+ If `tymio-mcp` is not on `PATH`, use `node` with an absolute path to `dist/index.js`.
47
+
48
+ ---
49
+
50
+ ## Alternative: remote MCP URL (no npm CLI)
51
+
52
+ If the host supports **URL** transport, point at the hub (OAuth handled by the IDE):
53
+
54
+ ```json
55
+ {
56
+ "mcpServers": {
57
+ "tymio": {
58
+ "url": "https://tymio.app/mcp"
59
+ }
60
+ }
61
+ }
62
+ ```
63
+
64
+ Replace the host when not using production.
65
+
66
+ ---
67
+
68
+ ## API-key mode (REST subset, intentional)
69
+
70
+ Set `DRD_API_KEY` (or `API_KEY`) and optionally `DRD_API_BASE_URL` (default `https://tymio.app`). Then `tymio-mcp` uses the REST bridge, **not** OAuth to the hosted MCP tool list.
71
+
72
+ ---
73
+
74
+ ## Environment reference
75
+
76
+ | Variable | Default | Purpose |
77
+ |----------|---------|---------|
78
+ | `TYMIO_MCP_URL` | `https://tymio.app/mcp` | Hosted MCP URL for OAuth proxy + `login` |
79
+ | `TYMIO_OAUTH_PORT` | `19876` | Loopback port for login callback |
80
+ | `TYMIO_MCP_QUIET` | unset | If set, suppress stderr hints when starting stdio |
81
+ | `DRD_API_KEY` / `API_KEY` | unset | If set → API-key REST bridge (subset), not OAuth proxy |
82
+ | `DRD_API_BASE_URL` | `https://tymio.app` | Hub origin for API-key bridge |
83
+
84
+ ---
85
+
86
+ ## Troubleshooting
87
+
88
+ - **401 / not signed in (stdio OAuth):** Run `tymio-mcp login` again.
89
+ - **User asks where to copy MCP API key:** Explain there is **no** such key in the UI; use **remote `/mcp` + OAuth** or **`tymio-mcp login`**.
90
+ - **Port in use on login:** Change `TYMIO_OAUTH_PORT` and re-run `login` (redirect URI must stay consistent).
91
+ - **Help:** `tymio-mcp help` — **full guide:** `tymio-mcp instructions`
92
+
93
+ ---
94
+
95
+ ## Machine-readable pointers
96
+
97
+ - **JSON (public):** `GET https://tymio.app/api/mcp/agent-context` — includes `tymioMcpCliAgentGuidanceMarkdown` (this file’s contents when the server can read it from disk).
98
+ - **Markdown site summary:** `https://tymio.app/llms.txt`
99
+ - **Repository:** `mcp/README.md`, `docs/HUB.md` §6, `docs/CODING_AGENT_HANDOFF_TYMIO_APP.md`
package/dist/api.js CHANGED
@@ -1,7 +1,8 @@
1
1
  /**
2
2
  * Minimal Tymio hub API client for the stdio MCP server. Uses DRD_API_BASE_URL and DRD_API_KEY from env.
3
3
  */
4
- const baseUrl = process.env.DRD_API_BASE_URL ?? "http://localhost:8080";
4
+ /** Hub origin (no `/mcp` path). Stdio bridge calls REST under `/api/...`. */
5
+ const baseUrl = process.env.DRD_API_BASE_URL ?? "https://tymio.app";
5
6
  const apiKey = process.env.DRD_API_KEY ?? process.env.API_KEY ?? "";
6
7
  function headers() {
7
8
  const h = { "Content-Type": "application/json" };
@@ -0,0 +1 @@
1
+ export declare function runApiKeyStdio(): Promise<void>;
@@ -0,0 +1,276 @@
1
+ /**
2
+ * REST/API-key stdio bridge (subset of hub tools). Set DRD_API_BASE_URL + DRD_API_KEY (or API_KEY).
3
+ */
4
+ import { z } from "zod";
5
+ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
6
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
7
+ import { drdFetch, drdFetchText, getBaseUrl, hasApiKey } from "./api.js";
8
+ import { AGENT_INSTRUCTIONS } from "./cliMessages.js";
9
+ import { toolTextWithFeedback } from "./mcpFeedbackFooter.js";
10
+ import { writeStdioStartupHint } from "./stdioHints.js";
11
+ export async function runApiKeyStdio() {
12
+ writeStdioStartupHint("api-key");
13
+ const server = new McpServer({ name: "tymio-hub", version: "1.0.0" }, { instructions: AGENT_INSTRUCTIONS });
14
+ async function textContent(text) {
15
+ return toolTextWithFeedback(getBaseUrl(), text);
16
+ }
17
+ // --- Health & meta (no auth required for health)
18
+ server.registerTool("drd_health", {
19
+ title: "Tymio API health check",
20
+ description: "Check if the Tymio hub API is reachable.",
21
+ inputSchema: z.object({})
22
+ }, async () => {
23
+ const data = await drdFetch("/api/health");
24
+ return textContent(JSON.stringify(data));
25
+ });
26
+ server.registerTool("drd_meta", {
27
+ title: "Get Tymio meta",
28
+ description: "Get meta data: domains, products, accounts, partners, personas, revenue streams, users.",
29
+ inputSchema: z.object({})
30
+ }, async () => {
31
+ const data = await drdFetch("/api/meta");
32
+ return textContent(JSON.stringify(data, null, 2));
33
+ });
34
+ // --- Initiatives
35
+ const listInitiativesSchema = z.object({
36
+ domainId: z.string().optional(),
37
+ ownerId: z.string().optional(),
38
+ horizon: z.enum(["NOW", "NEXT", "LATER"]).optional(),
39
+ priority: z.enum(["P0", "P1", "P2", "P3"]).optional(),
40
+ isGap: z.boolean().optional()
41
+ });
42
+ server.registerTool("drd_list_initiatives", {
43
+ title: "List initiatives",
44
+ description: "List initiatives with optional filters: domainId, ownerId, horizon, priority, isGap.",
45
+ inputSchema: listInitiativesSchema
46
+ }, async (args) => {
47
+ const params = new URLSearchParams();
48
+ if (args.domainId)
49
+ params.set("domainId", args.domainId);
50
+ if (args.ownerId)
51
+ params.set("ownerId", args.ownerId);
52
+ if (args.horizon)
53
+ params.set("horizon", args.horizon);
54
+ if (args.priority)
55
+ params.set("priority", args.priority);
56
+ if (args.isGap !== undefined)
57
+ params.set("isGap", String(args.isGap));
58
+ const data = await drdFetch(`/api/initiatives?${params.toString()}`);
59
+ return textContent(JSON.stringify(data.initiatives, null, 2));
60
+ });
61
+ server.registerTool("drd_get_initiative", {
62
+ title: "Get initiative by ID",
63
+ description: "Get a single initiative by its ID.",
64
+ inputSchema: z.object({ id: z.string().describe("Initiative ID") })
65
+ }, async ({ id }) => {
66
+ const data = await drdFetch(`/api/initiatives/${id}`);
67
+ return textContent(JSON.stringify(data.initiative, null, 2));
68
+ });
69
+ server.registerTool("drd_create_initiative", {
70
+ title: "Create initiative",
71
+ description: "Create a new initiative. Requires admin/editor role.",
72
+ inputSchema: z.object({
73
+ title: z.string(),
74
+ domainId: z.string(),
75
+ description: z.string().optional(),
76
+ ownerId: z.string().optional(),
77
+ priority: z.enum(["P0", "P1", "P2", "P3"]).optional(),
78
+ horizon: z.enum(["NOW", "NEXT", "LATER"]).optional(),
79
+ status: z.enum(["IDEA", "PLANNED", "IN_PROGRESS", "DONE", "BLOCKED"]).optional(),
80
+ commercialType: z.string().optional(),
81
+ isGap: z.boolean().optional()
82
+ })
83
+ }, async (body) => {
84
+ const data = await drdFetch("/api/initiatives", {
85
+ method: "POST",
86
+ body: JSON.stringify(body)
87
+ });
88
+ return textContent(JSON.stringify(data.initiative, null, 2));
89
+ });
90
+ server.registerTool("drd_update_initiative", {
91
+ title: "Update initiative",
92
+ description: "Update an existing initiative by ID.",
93
+ inputSchema: z.object({
94
+ id: z.string(),
95
+ title: z.string().optional(),
96
+ domainId: z.string().optional(),
97
+ description: z.string().optional(),
98
+ ownerId: z.string().optional(),
99
+ priority: z.enum(["P0", "P1", "P2", "P3"]).optional(),
100
+ horizon: z.enum(["NOW", "NEXT", "LATER"]).optional(),
101
+ status: z.enum(["IDEA", "PLANNED", "IN_PROGRESS", "DONE", "BLOCKED"]).optional(),
102
+ commercialType: z.string().optional(),
103
+ isGap: z.boolean().optional()
104
+ })
105
+ }, async ({ id, ...body }) => {
106
+ const data = await drdFetch(`/api/initiatives/${id}`, {
107
+ method: "PUT",
108
+ body: JSON.stringify(body)
109
+ });
110
+ return textContent(JSON.stringify(data.initiative, null, 2));
111
+ });
112
+ server.registerTool("drd_delete_initiative", {
113
+ title: "Delete initiative",
114
+ description: "Delete an initiative by ID.",
115
+ inputSchema: z.object({ id: z.string() })
116
+ }, async ({ id }) => {
117
+ await drdFetch(`/api/initiatives/${id}`, { method: "DELETE" });
118
+ return textContent(JSON.stringify({ ok: true }));
119
+ });
120
+ // --- Domains, products, personas
121
+ server.registerTool("drd_list_domains", {
122
+ title: "List domains",
123
+ description: "List all domains.",
124
+ inputSchema: z.object({})
125
+ }, async () => {
126
+ const data = await drdFetch("/api/domains");
127
+ return textContent(JSON.stringify(data.domains, null, 2));
128
+ });
129
+ server.registerTool("drd_create_domain", {
130
+ title: "Create domain",
131
+ description: "Create a new domain (pillar). Requires workspace OWNER or ADMIN.",
132
+ inputSchema: z.object({
133
+ name: z.string().min(1),
134
+ color: z.string().min(1),
135
+ sortOrder: z.number().int().optional()
136
+ })
137
+ }, async (body) => {
138
+ const data = await drdFetch("/api/domains", {
139
+ method: "POST",
140
+ body: JSON.stringify({
141
+ name: body.name,
142
+ color: body.color,
143
+ sortOrder: body.sortOrder ?? 0
144
+ })
145
+ });
146
+ return textContent(JSON.stringify(data.domain, null, 2));
147
+ });
148
+ server.registerTool("drd_list_products", {
149
+ title: "List products",
150
+ description: "List all products (with hierarchy).",
151
+ inputSchema: z.object({})
152
+ }, async () => {
153
+ const data = await drdFetch("/api/products");
154
+ return textContent(JSON.stringify(data.products, null, 2));
155
+ });
156
+ server.registerTool("drd_list_personas", {
157
+ title: "List personas",
158
+ description: "List all personas.",
159
+ inputSchema: z.object({})
160
+ }, async () => {
161
+ const data = await drdFetch("/api/personas");
162
+ return textContent(JSON.stringify(data.personas, null, 2));
163
+ });
164
+ server.registerTool("drd_list_accounts", {
165
+ title: "List accounts",
166
+ description: "List all accounts.",
167
+ inputSchema: z.object({})
168
+ }, async () => {
169
+ const data = await drdFetch("/api/accounts");
170
+ return textContent(JSON.stringify(data.accounts, null, 2));
171
+ });
172
+ server.registerTool("drd_list_partners", {
173
+ title: "List partners",
174
+ description: "List all partners.",
175
+ inputSchema: z.object({})
176
+ }, async () => {
177
+ const data = await drdFetch("/api/partners");
178
+ return textContent(JSON.stringify(data.partners, null, 2));
179
+ });
180
+ // --- KPIs, milestones, stakeholders
181
+ server.registerTool("drd_list_kpis", {
182
+ title: "List KPIs",
183
+ description: "List all initiative KPIs with their initiative context (title, domain, owner).",
184
+ inputSchema: z.object({})
185
+ }, async () => {
186
+ const data = await drdFetch("/api/kpis");
187
+ return textContent(JSON.stringify(data.kpis, null, 2));
188
+ });
189
+ server.registerTool("drd_list_milestones", {
190
+ title: "List milestones",
191
+ description: "List all initiative milestones with their initiative context.",
192
+ inputSchema: z.object({})
193
+ }, async () => {
194
+ const data = await drdFetch("/api/milestones");
195
+ return textContent(JSON.stringify(data.milestones, null, 2));
196
+ });
197
+ server.registerTool("drd_list_demands", {
198
+ title: "List demands",
199
+ description: "List all demands (from accounts, partners, internal, compliance).",
200
+ inputSchema: z.object({})
201
+ }, async () => {
202
+ const data = await drdFetch("/api/demands");
203
+ return textContent(JSON.stringify(data.demands, null, 2));
204
+ });
205
+ server.registerTool("drd_list_revenue_streams", {
206
+ title: "List revenue streams",
207
+ description: "List all revenue streams.",
208
+ inputSchema: z.object({})
209
+ }, async () => {
210
+ const data = await drdFetch("/api/revenue-streams");
211
+ return textContent(JSON.stringify(data.revenueStreams, null, 2));
212
+ });
213
+ server.registerTool("tymio_get_coding_agent_guide", {
214
+ title: "Get Tymio coding agent playbook (Markdown)",
215
+ description: "Full docs/CODING_AGENT_TYMIO.md: MCP usage, as-is to Tymio, feature lifecycle. Call at session start when automating this hub.",
216
+ inputSchema: z.object({})
217
+ }, async () => {
218
+ const md = await drdFetchText("/api/agent/coding-guide");
219
+ return textContent(md);
220
+ });
221
+ server.registerTool("tymio_get_agent_brief", {
222
+ title: "Get compiled agent capability brief",
223
+ description: "Returns the hub capability ontology as Markdown or JSON. mode=compact|full, format=md|json.",
224
+ inputSchema: z.object({
225
+ mode: z.enum(["compact", "full"]).default("compact"),
226
+ format: z.enum(["md", "json"]).default("md")
227
+ })
228
+ }, async (args) => {
229
+ const params = new URLSearchParams({ mode: args.mode, format: args.format });
230
+ const q = params.toString();
231
+ if (args.format === "md") {
232
+ const text = await drdFetchText(`/api/ontology/brief?${q}`);
233
+ return textContent(text);
234
+ }
235
+ const raw = await drdFetchText(`/api/ontology/brief?${q}`);
236
+ try {
237
+ const parsed = JSON.parse(raw);
238
+ return textContent(JSON.stringify(parsed, null, 2));
239
+ }
240
+ catch {
241
+ return textContent(raw);
242
+ }
243
+ });
244
+ server.registerTool("tymio_list_capabilities", {
245
+ title: "List hub capabilities (ontology)",
246
+ description: "Optional status: ACTIVE, DRAFT, DEPRECATED.",
247
+ inputSchema: z.object({ status: z.enum(["ACTIVE", "DRAFT", "DEPRECATED"]).optional() })
248
+ }, async (args) => {
249
+ const params = new URLSearchParams();
250
+ if (args.status)
251
+ params.set("status", args.status);
252
+ const q = params.toString();
253
+ const data = await drdFetch(`/api/ontology/capabilities${q ? `?${q}` : ""}`);
254
+ return textContent(JSON.stringify(data, null, 2));
255
+ });
256
+ server.registerTool("tymio_get_capability", {
257
+ title: "Get one capability by id or slug",
258
+ description: "Provide id or slug.",
259
+ inputSchema: z.object({ id: z.string().optional(), slug: z.string().optional() })
260
+ }, async (args) => {
261
+ if (args.id) {
262
+ const data = await drdFetch(`/api/ontology/capabilities/${args.id}`);
263
+ return textContent(JSON.stringify(data, null, 2));
264
+ }
265
+ if (args.slug) {
266
+ const data = await drdFetch(`/api/ontology/capabilities/by-slug/${encodeURIComponent(args.slug)}`);
267
+ return textContent(JSON.stringify(data, null, 2));
268
+ }
269
+ throw new Error("Provide id or slug");
270
+ });
271
+ if (!hasApiKey()) {
272
+ process.stderr.write("Warning: DRD_API_KEY is not set. Authenticated API calls will fail. Set DRD_API_KEY and API_KEY on the server.\n");
273
+ }
274
+ const transport = new StdioServerTransport();
275
+ await server.connect(transport);
276
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1 @@
1
+ export declare function runCli(argv: string[]): Promise<void>;
package/dist/cli.js ADDED
@@ -0,0 +1,35 @@
1
+ import { defaultMcpUrl } from "./configPaths.js";
2
+ import { AGENT_INSTRUCTIONS, HELP_SUMMARY } from "./cliMessages.js";
3
+ import { runApiKeyStdio } from "./apiKeyStdio.js";
4
+ import { runHubOAuthStdio } from "./hubProxyStdio.js";
5
+ import { runLoginCommand } from "./loginCommand.js";
6
+ import { removeAllOAuthFiles } from "./fileOAuthProvider.js";
7
+ function useApiKeyBridge() {
8
+ return Boolean(process.env.DRD_API_KEY?.trim() || process.env.API_KEY?.trim());
9
+ }
10
+ export async function runCli(argv) {
11
+ const args = argv.slice(2).filter((a) => a !== "--");
12
+ if (args[0] === "login") {
13
+ const url = args[1] ? new URL(args[1]) : defaultMcpUrl();
14
+ await runLoginCommand(url);
15
+ return;
16
+ }
17
+ if (args[0] === "logout") {
18
+ removeAllOAuthFiles();
19
+ process.stderr.write("Removed stored Tymio MCP OAuth credentials.\n");
20
+ return;
21
+ }
22
+ if (args[0] === "instructions" || args[0] === "guide") {
23
+ process.stderr.write(`${AGENT_INSTRUCTIONS}\n`);
24
+ return;
25
+ }
26
+ if (args[0] === "help" || args[0] === "-h" || args[0] === "--help") {
27
+ process.stderr.write(`${HELP_SUMMARY}\n`);
28
+ return;
29
+ }
30
+ if (useApiKeyBridge()) {
31
+ await runApiKeyStdio();
32
+ return;
33
+ }
34
+ await runHubOAuthStdio();
35
+ }
@@ -0,0 +1,7 @@
1
+ /** Short usage (stderr) — keep in sync with guidance file for agents. */
2
+ export declare const HELP_SUMMARY = "Tymio MCP CLI (@tymio/mcp-server)\n\nCommands:\n tymio-mcp Start stdio MCP (default: OAuth \u2192 hosted Tymio MCP)\n tymio-mcp login [url] Sign in with Google (browser). Saves tokens locally.\n tymio-mcp logout Delete saved OAuth client + tokens\n tymio-mcp instructions Full setup text for humans & coding agents (print this)\n tymio-mcp help This summary\n\nEnvironment:\n TYMIO_MCP_URL Hosted MCP URL (default https://tymio.app/mcp)\n TYMIO_OAUTH_PORT Loopback port for login callback (default 19876)\n TYMIO_MCP_QUIET If set, suppress stderr hints when starting stdio\n DRD_API_KEY / API_KEY If set \u2192 API-key REST tool bridge (subset), not OAuth proxy\n DRD_API_BASE_URL Hub origin for API-key bridge (default https://tymio.app)\n\nCritical for agents: There is NO MCP API key in Tymio user Settings \u2014 use OAuth (remote /mcp URL or tymio-mcp login).\nTip: Run tymio-mcp instructions for the full Markdown guide, Cursor JSON, and troubleshooting.\n";
3
+ /**
4
+ * Long-form instructions for coding agents and operators (Markdown).
5
+ * Loaded from `TYMIO_MCP_CLI_AGENT_GUIDANCE.md` beside the installed package / dist output.
6
+ */
7
+ export declare const AGENT_INSTRUCTIONS: string;