@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 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, then send a setup_complete signal with capture_session_signal. list_mcp_resources/list_mcp_resource_templates may be empty for this tool-driven connector and are not failure signals.`;
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
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@askthew/mcp-plugin",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "private": false,
5
5
  "description": "Ask The W MCP connector for capturing compact coding-agent session signals.",
6
6
  "type": "module",