@askthew/mcp-plugin 0.2.2 → 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 +17 -2
- package/dist/install.d.ts +9 -0
- package/dist/install.js +66 -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, sendInstallHeartbeat, } 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,11 +111,20 @@ 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;
|
|
108
120
|
const heartbeatSent = result.wroteFile
|
|
109
121
|
? await sendInstallHeartbeat(options).catch(() => false)
|
|
110
122
|
: false;
|
|
111
123
|
console.log(result.wroteFile ? "AskTheW plugin install complete." : "AskTheW plugin dry run complete.");
|
|
112
124
|
console.log(`Settings path: ${result.settingsPath}`);
|
|
125
|
+
if (instructions) {
|
|
126
|
+
console.log(`Agent instructions: ${instructions.path}`);
|
|
127
|
+
}
|
|
113
128
|
console.log(`Install command: ${formatInstallCommand(options)}`);
|
|
114
129
|
if (result.wroteFile) {
|
|
115
130
|
console.log(heartbeatSent
|
package/dist/install.d.ts
CHANGED
|
@@ -66,4 +66,13 @@ export declare function sendInstallHeartbeat(input: HostConfigInput & {
|
|
|
66
66
|
cwd?: string;
|
|
67
67
|
fetchImpl?: typeof fetch;
|
|
68
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
|
+
};
|
|
69
78
|
export {};
|
package/dist/install.js
CHANGED
|
@@ -2,6 +2,8 @@ import fs from "node:fs";
|
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { resolvePluginScope } from "./scope.js";
|
|
5
|
+
const ASKTHEW_INSTRUCTIONS_START = "<!-- ASKTHEW_PLUGIN_INSTRUCTIONS_START -->";
|
|
6
|
+
const ASKTHEW_INSTRUCTIONS_END = "<!-- ASKTHEW_PLUGIN_INSTRUCTIONS_END -->";
|
|
5
7
|
function isRecord(value) {
|
|
6
8
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
7
9
|
}
|
|
@@ -69,7 +71,7 @@ export function formatInstallCommand(input) {
|
|
|
69
71
|
}
|
|
70
72
|
export function verificationNextStep(hostType) {
|
|
71
73
|
const hostLabel = hostType === "claude_code" ? "Claude Code" : "Codex";
|
|
72
|
-
return `Refresh Ask The W to confirm the plugin was seen. 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.`;
|
|
73
75
|
}
|
|
74
76
|
export function installHostConfig(input) {
|
|
75
77
|
const settingsPath = resolveSettingsPath({
|
|
@@ -133,3 +135,66 @@ export async function sendInstallHeartbeat(input) {
|
|
|
133
135
|
});
|
|
134
136
|
return response.ok;
|
|
135
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
|
+
}
|