@askthew/mcp-plugin 0.2.1 → 0.2.3
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 +15 -0
- package/dist/cli.js +25 -2
- package/dist/install.d.ts +13 -0
- package/dist/install.js +90 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@ This package runs a small MCP server that lets Codex, Claude Code, Cursor, and o
|
|
|
8
8
|
|
|
9
9
|
- Installs an Ask The W MCP server entry into a supported local client.
|
|
10
10
|
- Preserves existing MCP servers and settings.
|
|
11
|
+
- Adds marked project instructions so future coding-agent sessions know when to send Ask The W updates.
|
|
11
12
|
- Sends a startup heartbeat so Ask The W can show that the plugin was seen.
|
|
12
13
|
- Exposes one primary MCP tool: `capture_session_signal`.
|
|
13
14
|
- Redacts obvious secrets from summaries, evidence excerpts, commands, and metadata before sending.
|
|
@@ -58,6 +59,20 @@ npx -y @askthew/mcp-plugin@latest install \
|
|
|
58
59
|
|
|
59
60
|
After install, restart or reload your coding agent if needed.
|
|
60
61
|
|
|
62
|
+
The installer also adds safe, marked project instructions:
|
|
63
|
+
|
|
64
|
+
- Codex: `AGENTS.md`
|
|
65
|
+
- Claude Code: `CLAUDE.md`
|
|
66
|
+
- Cursor: `.cursor/rules/askthew.mdc`
|
|
67
|
+
|
|
68
|
+
These instructions tell the coding agent to send compact Ask The W updates after meaningful direction changes, implementation work, verification, long-session checkpoints, and final summaries. Existing instruction files are preserved.
|
|
69
|
+
|
|
70
|
+
To skip this behavior, pass:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
--no-agent-instructions
|
|
74
|
+
```
|
|
75
|
+
|
|
61
76
|
## Configuration
|
|
62
77
|
|
|
63
78
|
The installer writes an MCP server entry like this:
|
package/dist/cli.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
3
|
import { createAskTheWMcpServer } from "./index.js";
|
|
4
|
-
import { createHostConfigSnippet, formatInstallCommand, installHostConfig, } from "./install.js";
|
|
4
|
+
import { createHostConfigSnippet, formatInstallCommand, installBehaviorInstructions, installHostConfig, sendInstallHeartbeat, } from "./install.js";
|
|
5
5
|
function usage() {
|
|
6
6
|
return [
|
|
7
7
|
"AskTheW Coding Agent Connector",
|
|
8
8
|
"",
|
|
9
9
|
"Usage:",
|
|
10
10
|
" askthew-mcp",
|
|
11
|
-
" askthew-mcp install --host <claude_code|codex|cursor> --token <install-token> --api-url <url> --server-name <name> [--client-id <id>] [--client-label <label>] [--dry-run]",
|
|
11
|
+
" askthew-mcp install --host <claude_code|codex|cursor> --token <install-token> --api-url <url> --server-name <name> [--client-id <id>] [--client-label <label>] [--dry-run] [--no-agent-instructions]",
|
|
12
12
|
" askthew-mcp print-config --host <claude_code|codex|cursor> --token <install-token> --api-url <url> --server-name <name> [--client-id <id>] [--client-label <label>]",
|
|
13
13
|
].join("\n");
|
|
14
14
|
}
|
|
@@ -20,12 +20,17 @@ function parseInstallArgs(argv) {
|
|
|
20
20
|
let apiUrl = process.env.ASKTHEW_API_URL?.trim() || "";
|
|
21
21
|
let serverName = process.env.ASKTHEW_SERVER_NAME?.trim() || "";
|
|
22
22
|
let dryRun = false;
|
|
23
|
+
let installAgentInstructions = true;
|
|
23
24
|
for (let index = 0; index < argv.length; index += 1) {
|
|
24
25
|
const argument = argv[index];
|
|
25
26
|
if (argument === "--dry-run") {
|
|
26
27
|
dryRun = true;
|
|
27
28
|
continue;
|
|
28
29
|
}
|
|
30
|
+
if (argument === "--no-agent-instructions") {
|
|
31
|
+
installAgentInstructions = false;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
29
34
|
const next = argv[index + 1];
|
|
30
35
|
if (!next) {
|
|
31
36
|
throw new Error(`Missing value for ${argument}.`);
|
|
@@ -85,6 +90,7 @@ function parseInstallArgs(argv) {
|
|
|
85
90
|
apiUrl,
|
|
86
91
|
serverName,
|
|
87
92
|
dryRun,
|
|
93
|
+
installAgentInstructions,
|
|
88
94
|
};
|
|
89
95
|
}
|
|
90
96
|
function normalizeInstallToken(token) {
|
|
@@ -105,9 +111,26 @@ async function main() {
|
|
|
105
111
|
if (command === "install") {
|
|
106
112
|
const options = parseInstallArgs(argv);
|
|
107
113
|
const result = installHostConfig(options);
|
|
114
|
+
const instructions = options.installAgentInstructions
|
|
115
|
+
? installBehaviorInstructions({
|
|
116
|
+
hostType: options.hostType,
|
|
117
|
+
dryRun: options.dryRun,
|
|
118
|
+
})
|
|
119
|
+
: null;
|
|
120
|
+
const heartbeatSent = result.wroteFile
|
|
121
|
+
? await sendInstallHeartbeat(options).catch(() => false)
|
|
122
|
+
: false;
|
|
108
123
|
console.log(result.wroteFile ? "AskTheW plugin install complete." : "AskTheW plugin dry run complete.");
|
|
109
124
|
console.log(`Settings path: ${result.settingsPath}`);
|
|
125
|
+
if (instructions) {
|
|
126
|
+
console.log(`Agent instructions: ${instructions.path}`);
|
|
127
|
+
}
|
|
110
128
|
console.log(`Install command: ${formatInstallCommand(options)}`);
|
|
129
|
+
if (result.wroteFile) {
|
|
130
|
+
console.log(heartbeatSent
|
|
131
|
+
? "Ask The W setup check sent. Refresh the app to confirm the plugin was seen."
|
|
132
|
+
: "Ask The W setup check could not be sent yet. Restart or reload your coding app, then refresh Ask The W.");
|
|
133
|
+
}
|
|
111
134
|
console.log(`Next step: ${result.nextStep}`);
|
|
112
135
|
if (!result.wroteFile) {
|
|
113
136
|
console.log("");
|
package/dist/install.d.ts
CHANGED
|
@@ -62,4 +62,17 @@ export declare function installHostConfig(input: InstallHostConfigInput): {
|
|
|
62
62
|
wroteFile: boolean;
|
|
63
63
|
nextStep: string;
|
|
64
64
|
};
|
|
65
|
+
export declare function sendInstallHeartbeat(input: HostConfigInput & {
|
|
66
|
+
cwd?: string;
|
|
67
|
+
fetchImpl?: typeof fetch;
|
|
68
|
+
}): Promise<boolean>;
|
|
69
|
+
export declare function installBehaviorInstructions(input: {
|
|
70
|
+
hostType: SupportedHostType;
|
|
71
|
+
cwd?: string;
|
|
72
|
+
dryRun?: boolean;
|
|
73
|
+
}): {
|
|
74
|
+
path: string;
|
|
75
|
+
wroteFile: boolean;
|
|
76
|
+
content: string;
|
|
77
|
+
};
|
|
65
78
|
export {};
|
package/dist/install.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { resolvePluginScope } from "./scope.js";
|
|
5
|
+
const ASKTHEW_INSTRUCTIONS_START = "<!-- ASKTHEW_PLUGIN_INSTRUCTIONS_START -->";
|
|
6
|
+
const ASKTHEW_INSTRUCTIONS_END = "<!-- ASKTHEW_PLUGIN_INSTRUCTIONS_END -->";
|
|
4
7
|
function isRecord(value) {
|
|
5
8
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6
9
|
}
|
|
@@ -68,7 +71,7 @@ export function formatInstallCommand(input) {
|
|
|
68
71
|
}
|
|
69
72
|
export function verificationNextStep(hostType) {
|
|
70
73
|
const hostLabel = hostType === "claude_code" ? "Claude Code" : "Codex";
|
|
71
|
-
return `Restart ${hostLabel} if it is already open
|
|
74
|
+
return `Refresh Ask The W to confirm the plugin was seen. Restart ${hostLabel} if it is already open. The installed project instructions tell the coding agent when to send Ask The W updates automatically. list_mcp_resources/list_mcp_resource_templates may be empty for this tool-driven connector and are not failure signals.`;
|
|
72
75
|
}
|
|
73
76
|
export function installHostConfig(input) {
|
|
74
77
|
const settingsPath = resolveSettingsPath({
|
|
@@ -109,3 +112,89 @@ export function installHostConfig(input) {
|
|
|
109
112
|
nextStep: verificationNextStep(input.hostType),
|
|
110
113
|
};
|
|
111
114
|
}
|
|
115
|
+
export async function sendInstallHeartbeat(input) {
|
|
116
|
+
const fetcher = input.fetchImpl ?? fetch;
|
|
117
|
+
const scope = resolvePluginScope(input.cwd ?? process.cwd());
|
|
118
|
+
const apiUrl = input.apiUrl.replace(/\/$/, "");
|
|
119
|
+
const response = await fetcher(`${apiUrl}/api/connectors/mcp/heartbeat`, {
|
|
120
|
+
method: "POST",
|
|
121
|
+
headers: {
|
|
122
|
+
"Content-Type": "application/json",
|
|
123
|
+
},
|
|
124
|
+
body: JSON.stringify({
|
|
125
|
+
installToken: input.token,
|
|
126
|
+
clientId: input.clientId || input.hostType,
|
|
127
|
+
clientLabel: input.clientLabel,
|
|
128
|
+
hostType: input.hostType,
|
|
129
|
+
serverName: input.serverName,
|
|
130
|
+
repoName: scope.repoName,
|
|
131
|
+
...(scope.repoRoot ? { repoRoot: scope.repoRoot } : {}),
|
|
132
|
+
...(scope.appPath ? { appPath: scope.appPath } : {}),
|
|
133
|
+
...(scope.serviceName ? { serviceName: scope.serviceName } : {}),
|
|
134
|
+
}),
|
|
135
|
+
});
|
|
136
|
+
return response.ok;
|
|
137
|
+
}
|
|
138
|
+
function behaviorInstructions(hostType) {
|
|
139
|
+
const hostLabel = hostType === "claude_code" ? "Claude Code" : hostType === "cursor" ? "Cursor" : "Codex";
|
|
140
|
+
return [
|
|
141
|
+
ASKTHEW_INSTRUCTIONS_START,
|
|
142
|
+
"",
|
|
143
|
+
"## Ask The W Plugin",
|
|
144
|
+
"",
|
|
145
|
+
`This workspace uses the Ask The W Plugin. When working in this repo with ${hostLabel}, send compact Ask The W updates automatically with the MCP tool \`capture_session_signal\`.`,
|
|
146
|
+
"",
|
|
147
|
+
"Send an update:",
|
|
148
|
+
"- after the user accepts or rejects product, architecture, or implementation direction",
|
|
149
|
+
"- after meaningful implementation changes",
|
|
150
|
+
"- after tests, builds, lint, or other verification",
|
|
151
|
+
"- every 8-12 turns in long sessions",
|
|
152
|
+
"- at the final summary",
|
|
153
|
+
"",
|
|
154
|
+
"Keep updates compact: short summary, minimal evidence excerpts, files touched, commands run, and useful metadata. Do not send full transcripts. Redact obvious secrets before sending.",
|
|
155
|
+
"",
|
|
156
|
+
"If this is the first session after install or reload, send a `setup_complete` update first.",
|
|
157
|
+
"",
|
|
158
|
+
ASKTHEW_INSTRUCTIONS_END,
|
|
159
|
+
"",
|
|
160
|
+
].join("\n");
|
|
161
|
+
}
|
|
162
|
+
function cursorBehaviorInstructions() {
|
|
163
|
+
return [
|
|
164
|
+
"---",
|
|
165
|
+
"description: Ask The W Plugin capture rules",
|
|
166
|
+
"alwaysApply: true",
|
|
167
|
+
"---",
|
|
168
|
+
"",
|
|
169
|
+
behaviorInstructions("cursor"),
|
|
170
|
+
].join("\n");
|
|
171
|
+
}
|
|
172
|
+
function upsertMarkedBlock(existing, block) {
|
|
173
|
+
const startIndex = existing.indexOf(ASKTHEW_INSTRUCTIONS_START);
|
|
174
|
+
const endIndex = existing.indexOf(ASKTHEW_INSTRUCTIONS_END);
|
|
175
|
+
if (startIndex !== -1 && endIndex !== -1 && endIndex > startIndex) {
|
|
176
|
+
const afterEnd = endIndex + ASKTHEW_INSTRUCTIONS_END.length;
|
|
177
|
+
return `${existing.slice(0, startIndex).trimEnd()}\n\n${block.trimEnd()}\n${existing.slice(afterEnd).trimStart()}`.trimEnd() + "\n";
|
|
178
|
+
}
|
|
179
|
+
return `${existing.trimEnd()}${existing.trim() ? "\n\n" : ""}${block.trimEnd()}\n`;
|
|
180
|
+
}
|
|
181
|
+
export function installBehaviorInstructions(input) {
|
|
182
|
+
const cwd = path.resolve(input.cwd ?? process.cwd());
|
|
183
|
+
const instructionsPath = input.hostType === "claude_code"
|
|
184
|
+
? path.join(cwd, "CLAUDE.md")
|
|
185
|
+
: input.hostType === "cursor"
|
|
186
|
+
? path.join(cwd, ".cursor", "rules", "askthew.mdc")
|
|
187
|
+
: path.join(cwd, "AGENTS.md");
|
|
188
|
+
const block = input.hostType === "cursor" ? cursorBehaviorInstructions() : behaviorInstructions(input.hostType);
|
|
189
|
+
const existing = fs.existsSync(instructionsPath) ? fs.readFileSync(instructionsPath, "utf8") : "";
|
|
190
|
+
const next = input.hostType === "cursor" ? block : upsertMarkedBlock(existing, block);
|
|
191
|
+
if (!input.dryRun) {
|
|
192
|
+
fs.mkdirSync(path.dirname(instructionsPath), { recursive: true });
|
|
193
|
+
fs.writeFileSync(instructionsPath, next, "utf8");
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
path: instructionsPath,
|
|
197
|
+
wroteFile: !input.dryRun,
|
|
198
|
+
content: next,
|
|
199
|
+
};
|
|
200
|
+
}
|