@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 +125 -42
- package/TYMIO_MCP_CLI_AGENT_GUIDANCE.md +99 -0
- package/dist/api.js +2 -1
- package/dist/apiKeyStdio.d.ts +1 -0
- package/dist/apiKeyStdio.js +276 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +35 -0
- package/dist/cliMessages.d.ts +7 -0
- package/dist/cliMessages.js +53 -0
- package/dist/configPaths.d.ts +6 -0
- package/dist/configPaths.js +32 -0
- package/dist/fileOAuthProvider.d.ts +27 -0
- package/dist/fileOAuthProvider.js +127 -0
- package/dist/hubProxyStdio.d.ts +4 -0
- package/dist/hubProxyStdio.js +59 -0
- package/dist/index.js +2 -279
- package/dist/loginCommand.d.ts +1 -0
- package/dist/loginCommand.js +30 -0
- package/dist/oauthCallbackServer.d.ts +10 -0
- package/dist/oauthCallbackServer.js +89 -0
- package/dist/stdioHints.d.ts +5 -0
- package/dist/stdioHints.js +16 -0
- package/package.json +17 -5
package/README.md
CHANGED
|
@@ -1,84 +1,167 @@
|
|
|
1
|
-
# Tymio MCP
|
|
1
|
+
# Tymio MCP CLI (`@tymio/mcp-server`)
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
12
|
+
## Quick start (OAuth, production)
|
|
10
13
|
|
|
11
|
-
1.
|
|
12
|
-
2.
|
|
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
|
-
|
|
17
|
+
```bash
|
|
18
|
+
tymio-mcp login
|
|
19
|
+
```
|
|
15
20
|
|
|
16
|
-
|
|
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
|
-
|
|
23
|
+
3. Point your MCP client at stdio **without** setting `DRD_API_KEY`:
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"mcpServers": {
|
|
28
|
+
"tymio": {
|
|
29
|
+
"command": "tymio-mcp",
|
|
30
|
+
"args": []
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
```
|
|
24
35
|
|
|
25
|
-
|
|
26
|
-
npm install -g @tymio/mcp-server
|
|
27
|
-
```
|
|
36
|
+
4. Optional: `tymio-mcp logout` removes saved OAuth files.
|
|
28
37
|
|
|
29
|
-
|
|
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
|
-
|
|
32
|
-
npm install -g /absolute/path/to/proproman/mcp
|
|
33
|
-
```
|
|
40
|
+
### OAuth callback port
|
|
34
41
|
|
|
35
|
-
|
|
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
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
61
|
+
Example:
|
|
47
62
|
|
|
48
63
|
```json
|
|
49
64
|
{
|
|
50
65
|
"mcpServers": {
|
|
51
|
-
"tymio-
|
|
66
|
+
"tymio-api-key": {
|
|
52
67
|
"command": "tymio-mcp",
|
|
53
68
|
"args": [],
|
|
54
69
|
"env": {
|
|
55
|
-
"
|
|
56
|
-
"
|
|
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
|
-
|
|
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
|
|
69
|
-
"
|
|
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
|
-
|
|
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
|
-
|
|
165
|
+
## Architecture reference
|
|
83
166
|
|
|
84
|
-
|
|
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
|
-
|
|
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;
|