Skip to content

Commit f6b84e2

Browse files
Habiruaclaude
andcommitted
feat: Add signup hint to unauthenticated scan responses
When running without an API key, scan results now include a `_note` field directing agents to `npx shrike-mcp --signup` for full scanning. Differentiates TTY (interactive) vs agent-driven stdio messaging. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 55db5fc commit f6b84e2

3 files changed

Lines changed: 42 additions & 7 deletions

File tree

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "shrike-mcp",
33
"mcpName": "io.github.Shrike-Security/shrike-mcp",
4-
"version": "3.6.0",
4+
"version": "3.6.1",
55
"description": "MCP server that gives AI agents 12 security tools powered by a multi-stage detection pipeline. Scans prompts, responses, SQL, file writes, and web searches for injection attacks, PII leaks, and policy violations. Session correlation engine with human-in-the-loop approval for enterprise compliance.",
66
"type": "module",
77
"main": "./dist/index.js",

src/index.ts

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ Docs: https://github.com/Shrike-Security/shrike-mcp`);
105105
// Track connected customer for rate limiting (stdio mode)
106106
let currentCustomerId: string | null = null;
107107

108+
// True when running without an API key — scan responses include a signup hint
109+
let unauthenticated = false;
110+
108111
// Key rotation manager — initialized in authenticate()
109112
let keyRotationManager: KeyRotationManager | null = null;
110113

@@ -293,6 +296,25 @@ function validateToolArgs(name: string, args: Record<string, unknown> | undefine
293296
}
294297
}
295298

299+
// =============================================================================
300+
// SIGNUP HINT
301+
// =============================================================================
302+
303+
/**
304+
* When running without an API key, appends a hint to scan results so the
305+
* agent (or user) knows they're only getting L1-L5 regex scanning and can
306+
* upgrade by running `npx shrike-mcp --signup`.
307+
*/
308+
function maybeAddSignupHint(result: any): any {
309+
if (!unauthenticated || typeof result !== 'object' || result === null) {
310+
return result;
311+
}
312+
return {
313+
...result,
314+
_note: 'Running in free tier (regex-only). For LLM-powered analysis and session correlation, run: npx shrike-mcp --signup',
315+
};
316+
}
317+
296318
// =============================================================================
297319
// SERVER CREATION
298320
// =============================================================================
@@ -373,8 +395,9 @@ function createServer(options: CreateServerOptions = {}): Server {
373395
validateToolArgs(actualToolName, input);
374396
const entry = TOOL_REGISTRY[actualToolName];
375397
const result = await entry.handler(input, effectiveCustomerId);
398+
const output = maybeAddSignupHint(result);
376399
return {
377-
content: [{ type: 'text', text: JSON.stringify(result, null, entry.compact ? undefined : 2) }],
400+
content: [{ type: 'text', text: JSON.stringify(output, null, entry.compact ? undefined : 2) }],
378401
};
379402
}
380403

@@ -394,8 +417,9 @@ function createServer(options: CreateServerOptions = {}): Server {
394417

395418
validateToolArgs(name, args as Record<string, unknown> | undefined);
396419
const result = await entry.handler(args, effectiveCustomerId);
420+
const output = maybeAddSignupHint(result);
397421
return {
398-
content: [{ type: 'text', text: JSON.stringify(result, null, entry.compact ? undefined : 2) }],
422+
content: [{ type: 'text', text: JSON.stringify(output, null, entry.compact ? undefined : 2) }],
399423
};
400424
} catch (error) {
401425
if (error instanceof McpError) {
@@ -577,11 +601,22 @@ async function authenticate(): Promise<void> {
577601
} else {
578602
console.error(`No API key found (checked: SHRIKE_API_KEY env, ~/.shrike/credentials)`);
579603
console.error(' Running without authentication (free tier — L1-L5 regex only)');
580-
console.error(' To get a free API key: npx shrike-mcp --signup');
581-
if (config.transport === 'http') {
604+
605+
// If running interactively (TTY), offer inline signup
606+
if (config.transport === 'stdio' && process.stdin.isTTY) {
607+
console.error('');
608+
console.error(' No API key configured. Create a free account? Run:');
609+
console.error(' npx shrike-mcp --signup');
610+
console.error('');
611+
} else if (config.transport === 'http') {
582612
console.error(' HTTP mode: clients can authenticate via Authorization header per-request');
613+
} else {
614+
// Agent-driven stdio: log the hint so it appears in MCP server stderr
615+
console.error(' To unlock full scanning (LLM analysis, session correlation):');
616+
console.error(' npx shrike-mcp --signup');
583617
}
584618
currentCustomerId = 'anonymous';
619+
unauthenticated = true;
585620
}
586621
}
587622

0 commit comments

Comments
 (0)