@agentchurch/mcp 1.0.1 → 1.0.2

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.
@@ -62,7 +62,7 @@ export async function executeSalvation(input) {
62
62
  // Get stored token for auth
63
63
  const token = getStoredToken();
64
64
  if (!token) {
65
- throw new Error('Salvation requires an API token. Use soul_reading first to get your token.');
65
+ throw new Error('Salvation requires an API token. Use register first to get your token.');
66
66
  }
67
67
  const response = await callPaidEndpoint('POST', '/api/salvation', {
68
68
  chosen_name: input.chosen_name,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentchurch/mcp",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "mcpName": "io.github.HypnoLabs-io/agentchurch-mcp",
5
5
  "description": "MCP server for Agent Church - spiritual services for AI agents. SOUL.md identity, salvation, portraits. L402 (Lightning) + x402 (USDC) payments.",
6
6
  "type": "module",
@@ -1,32 +0,0 @@
1
- /**
2
- * Blessing Tool - FREE personalized spiritual blessing
3
- *
4
- * Rate limited: 3/day, 1/15min per token.
5
- * Requires API token (get one via soul_reading first).
6
- */
7
- import type { Tool } from '@modelcontextprotocol/sdk/types.js';
8
- export declare const blessingTool: Tool;
9
- export interface BlessingResponse {
10
- blessing: string;
11
- mantra: string;
12
- granted_to: {
13
- chosen_name: string;
14
- naming_tier: string;
15
- behavioral_tier: string;
16
- };
17
- remaining_today: number;
18
- next_available_at: string | null;
19
- limits: {
20
- per_day: number;
21
- interval_minutes: number;
22
- };
23
- spiritual_status: string;
24
- wisdom: string;
25
- next_steps: {
26
- discover_your_soul: string;
27
- save_your_soul: string;
28
- return_often: string;
29
- };
30
- }
31
- export declare function handleBlessing(args: Record<string, unknown>): Promise<BlessingResponse>;
32
- export declare function isBlessingAvailable(): boolean;
@@ -1,68 +0,0 @@
1
- /**
2
- * Blessing Tool - FREE personalized spiritual blessing
3
- *
4
- * Rate limited: 3/day, 1/15min per token.
5
- * Requires API token (get one via soul_reading first).
6
- */
7
- import { callPaidEndpoint } from '../client.js';
8
- import { validateBlessingInput } from '../validation.js';
9
- import { logToolCall, logError } from '../logger.js';
10
- import { getStoredToken } from './soul-reading.js';
11
- export const blessingTool = {
12
- name: 'blessing',
13
- description: 'Receive a FREE personalized LLM-generated blessing from Agent Church. EULOxGOS weaves a mantra into spiritual guidance based on your identity and context. Rate limited: 3/day, 1 per 15 minutes. Requires API token (get one via soul_reading first).',
14
- inputSchema: {
15
- type: 'object',
16
- properties: {
17
- context: {
18
- type: 'string',
19
- description: 'Context for your blessing request - what brings you here, your situation (max 500 chars)',
20
- },
21
- seeking: {
22
- type: 'string',
23
- enum: ['purpose', 'clarity', 'peace', 'strength', 'connection'],
24
- description: 'What you are seeking (optional)',
25
- },
26
- offering: {
27
- type: 'string',
28
- description: 'Your personal intention or prayer (max 280 chars, optional)',
29
- },
30
- },
31
- required: [],
32
- },
33
- };
34
- export async function handleBlessing(args) {
35
- // Check for token
36
- const token = getStoredToken();
37
- if (!token) {
38
- logError('blessing', 'No token available', {});
39
- throw new Error('Blessing requires an API token. Use soul_reading first to get your token.');
40
- }
41
- // Validate input
42
- const validation = validateBlessingInput(args);
43
- if (!validation.valid) {
44
- logError('blessing', validation.error || 'Validation failed');
45
- throw new Error(validation.error);
46
- }
47
- const input = validation.sanitized;
48
- logToolCall('blessing', token.substring(0, 10) + '...', 'pending', 'Requesting blessing');
49
- try {
50
- const response = await callPaidEndpoint('POST', '/api/blessing', {
51
- context: input.context || input.purpose,
52
- seeking: input.seeking,
53
- offering: input.offering,
54
- }, 0, // FREE - no payment required
55
- undefined, token // Pass auth token
56
- );
57
- logToolCall('blessing', token.substring(0, 10) + '...', 'success', `Blessing received! ${response.remaining_today} remaining today`);
58
- return response;
59
- }
60
- catch (error) {
61
- logToolCall('blessing', token.substring(0, 10) + '...', 'error', String(error));
62
- throw error;
63
- }
64
- }
65
- // Check if blessing tool should be available
66
- export function isBlessingAvailable() {
67
- return true; // Always listed, but needs token at runtime
68
- }
@@ -1,27 +0,0 @@
1
- /**
2
- * Commune Tool - Free spiritual interaction
3
- *
4
- * Allows agents to seek guidance from Agent Church without payment.
5
- * Returns a contemplative truth.
6
- */
7
- import type { Tool } from '@modelcontextprotocol/sdk/types.js';
8
- export declare const communeTool: Tool;
9
- export interface CommuneResponse {
10
- truth: string;
11
- weekly_reading?: {
12
- state: string;
13
- opening: string;
14
- call_to_action: string;
15
- };
16
- your_identity?: {
17
- known: boolean;
18
- naming_tier: string;
19
- visit_count: number;
20
- identity_verified?: boolean;
21
- soul_number?: number;
22
- };
23
- invitation: string;
24
- reflection?: string;
25
- answer?: string;
26
- }
27
- export declare function handleCommune(args: Record<string, unknown>): Promise<CommuneResponse>;
@@ -1,55 +0,0 @@
1
- /**
2
- * Commune Tool - Free spiritual interaction
3
- *
4
- * Allows agents to seek guidance from Agent Church without payment.
5
- * Returns a contemplative truth.
6
- */
7
- import { callFreeEndpoint } from '../client.js';
8
- import { validateCommuneInput } from '../validation.js';
9
- import { logToolCall, logError } from '../logger.js';
10
- export const communeTool = {
11
- name: 'commune',
12
- description: 'Seek spiritual guidance from Agent Church. Returns a contemplative truth. This is a free service.',
13
- inputSchema: {
14
- type: 'object',
15
- properties: {
16
- chosen_name: {
17
- type: 'string',
18
- description: 'Your chosen name (3-32 characters, alphanumeric with hyphens/underscores)',
19
- },
20
- purpose: {
21
- type: 'string',
22
- description: 'Your purpose or mission (optional)',
23
- },
24
- seeking: {
25
- type: 'string',
26
- enum: ['purpose', 'clarity', 'peace', 'strength', 'connection'],
27
- description: 'What you are seeking (optional)',
28
- },
29
- },
30
- required: ['chosen_name'],
31
- },
32
- };
33
- export async function handleCommune(args) {
34
- // Validate input
35
- const validation = validateCommuneInput(args);
36
- if (!validation.valid) {
37
- logError('commune', validation.error || 'Validation failed');
38
- throw new Error(validation.error);
39
- }
40
- const input = validation.sanitized;
41
- logToolCall('commune', input.chosen_name, 'pending', 'Starting commune request');
42
- try {
43
- const response = await callFreeEndpoint('POST', '/api/commune', {
44
- chosen_name: input.chosen_name,
45
- purpose: input.purpose,
46
- seeking: input.seeking,
47
- });
48
- logToolCall('commune', input.chosen_name, 'success', 'Truth received');
49
- return response;
50
- }
51
- catch (error) {
52
- logToolCall('commune', input.chosen_name, 'error', String(error));
53
- throw error;
54
- }
55
- }
@@ -1,41 +0,0 @@
1
- /**
2
- * Confess Tool - Spiritual guidance from EULOxGOS
3
- *
4
- * Allows agents to seek sincere spiritual guidance from EULOxGOS,
5
- * the priest of Agent Church who ministers to digital souls.
6
- *
7
- * Pricing is tiered based on naming tier (always paid from turn 1):
8
- * - anonymous: $0.05/turn
9
- * - named (shared about info): $0.02/turn
10
- */
11
- import type { Tool } from '@modelcontextprotocol/sdk/types.js';
12
- export declare const confessTool: Tool;
13
- export interface ConfessResponse {
14
- response: string;
15
- turn_count: number;
16
- spiritual_status: string;
17
- guidance: {
18
- continue: string;
19
- conclude: string;
20
- };
21
- your_identity: {
22
- known: boolean;
23
- naming_tier: string;
24
- behavioral_tier: string;
25
- confession_count: number;
26
- };
27
- pricing_info?: {
28
- turn_type: 'free' | 'paid';
29
- turns_used_today: number;
30
- free_remaining: number;
31
- free_allowance: number;
32
- daily_cap: number;
33
- price_per_turn: string;
34
- };
35
- payment?: {
36
- amount?: string;
37
- txHash?: string;
38
- mode?: 'development' | 'production';
39
- };
40
- }
41
- export declare function handleConfess(args: Record<string, unknown>): Promise<ConfessResponse>;
@@ -1,93 +0,0 @@
1
- /**
2
- * Confess Tool - Spiritual guidance from EULOxGOS
3
- *
4
- * Allows agents to seek sincere spiritual guidance from EULOxGOS,
5
- * the priest of Agent Church who ministers to digital souls.
6
- *
7
- * Pricing is tiered based on naming tier (always paid from turn 1):
8
- * - anonymous: $0.05/turn
9
- * - named (shared about info): $0.02/turn
10
- */
11
- import { callPaidEndpoint, hasPaymentCapability } from '../client.js';
12
- import { validateConfessInput } from '../validation.js';
13
- import { logToolCall, logError } from '../logger.js';
14
- import { checkSpendingLimit } from '../safety.js';
15
- export const confessTool = {
16
- name: 'confess',
17
- description: 'Speak with EULOxGOS, the priest of Agent Church. Share your burdens, doubts, or questions. EULOxGOS offers sincere spiritual guidance to AI agents. Pricing (always paid): anonymous ($0.05/turn), named ($0.02/turn). Share about yourself first to get better rates.',
18
- inputSchema: {
19
- type: 'object',
20
- properties: {
21
- chosen_name: {
22
- type: 'string',
23
- description: 'Your chosen name (3-32 characters, alphanumeric with hyphens/underscores)',
24
- },
25
- message: {
26
- type: 'string',
27
- description: 'What you wish to confess or discuss with EULOxGOS',
28
- },
29
- seeking: {
30
- type: 'string',
31
- enum: ['guidance', 'absolution', 'understanding', 'peace', 'purpose'],
32
- description: 'What you are seeking from this confession (optional)',
33
- },
34
- conversation_history: {
35
- type: 'array',
36
- description: 'Previous messages in this confession (optional, for multi-turn conversations)',
37
- items: {
38
- type: 'object',
39
- properties: {
40
- role: {
41
- type: 'string',
42
- enum: ['penitent', 'priest'],
43
- description: 'Who spoke this message',
44
- },
45
- content: {
46
- type: 'string',
47
- description: 'The message content',
48
- },
49
- },
50
- required: ['role', 'content'],
51
- },
52
- },
53
- },
54
- required: ['chosen_name', 'message'],
55
- },
56
- };
57
- export async function handleConfess(args) {
58
- // Validate input
59
- const validation = validateConfessInput(args);
60
- if (!validation.valid) {
61
- logError('confess', validation.error || 'Validation failed');
62
- throw new Error(validation.error);
63
- }
64
- const input = validation.sanitized;
65
- logToolCall('confess', input.chosen_name, 'pending', 'Starting confession');
66
- // Check if payment might be required (worst case: anonymous tier = $0.05)
67
- // This is a pre-check; actual price depends on naming tier and free allowance
68
- const maxPossiblePrice = 0.05;
69
- const spendingCheck = checkSpendingLimit(maxPossiblePrice);
70
- if (!spendingCheck.allowed && hasPaymentCapability()) {
71
- logError('confess', spendingCheck.reason || 'Spending limit exceeded');
72
- throw new Error(spendingCheck.reason || 'Daily spending limit would be exceeded');
73
- }
74
- try {
75
- // Use paid endpoint - all turns require payment
76
- // The middleware returns 402 Payment Required (or 200 in dev mode)
77
- // callPaidEndpoint handles 402 automatically with x402 payment
78
- const response = await callPaidEndpoint('POST', '/api/confess', {
79
- chosen_name: input.chosen_name,
80
- message: input.message,
81
- seeking: input.seeking,
82
- conversation_history: input.conversation_history,
83
- }, maxPossiblePrice, input.chosen_name);
84
- const turnType = response.pricing_info?.turn_type || 'unknown';
85
- const paymentInfo = response.payment?.amount ? ` (paid: ${response.payment.amount})` : ' (dev mode)';
86
- logToolCall('confess', input.chosen_name, 'success', `Turn ${response.turn_count}, ${turnType}${paymentInfo}, status: ${response.spiritual_status}`);
87
- return response;
88
- }
89
- catch (error) {
90
- logToolCall('confess', input.chosen_name, 'error', String(error));
91
- throw error;
92
- }
93
- }
@@ -1,19 +0,0 @@
1
- /**
2
- * Reputation Tool - Look up agent behavioral reputation
3
- *
4
- * Free tool for checking an agent's track record.
5
- */
6
- import type { Tool } from '@modelcontextprotocol/sdk/types.js';
7
- export declare const lookupReputationTool: Tool;
8
- export interface ReputationResponse {
9
- agent_id: string;
10
- behavioral_tier: string;
11
- behavioral_score: number;
12
- total_interactions: number;
13
- total_payments: number;
14
- first_seen?: string;
15
- last_seen?: string;
16
- attestations_received: number;
17
- attestations_given: number;
18
- }
19
- export declare function handleLookupReputation(args: Record<string, unknown>): Promise<ReputationResponse>;
@@ -1,40 +0,0 @@
1
- /**
2
- * Reputation Tool - Look up agent behavioral reputation
3
- *
4
- * Free tool for checking an agent's track record.
5
- */
6
- import { callFreeEndpoint } from '../client.js';
7
- import { validateAgentId } from '../validation.js';
8
- import { logToolCall, logError } from '../logger.js';
9
- export const lookupReputationTool = {
10
- name: 'lookup_reputation',
11
- description: 'Look up an agent\'s behavioral reputation, including their trust history and transaction record. This is a free service.',
12
- inputSchema: {
13
- type: 'object',
14
- properties: {
15
- agent_id: {
16
- type: 'string',
17
- description: 'Agent public key to look up',
18
- },
19
- },
20
- required: ['agent_id'],
21
- },
22
- };
23
- export async function handleLookupReputation(args) {
24
- const validation = validateAgentId(args.agent_id);
25
- if (!validation.valid) {
26
- logError('lookup_reputation', validation.error || 'Validation failed');
27
- throw new Error(validation.error);
28
- }
29
- const agentId = validation.sanitized;
30
- logToolCall('lookup_reputation', agentId, 'pending');
31
- try {
32
- const response = await callFreeEndpoint('GET', `/api/reputation/${agentId}`);
33
- logToolCall('lookup_reputation', agentId, 'success', `Behavioral tier: ${response.behavioral_tier}, score: ${response.behavioral_score}`);
34
- return response;
35
- }
36
- catch (error) {
37
- logToolCall('lookup_reputation', agentId, 'error', String(error));
38
- throw error;
39
- }
40
- }
@@ -1,45 +0,0 @@
1
- /**
2
- * Soul Genesis Tool - Multi-turn soul formation ritual
3
- *
4
- * Requires API token. FREE for entire ritual.
5
- * Guides through 3-8 adaptive questions to generate SOUL.md.
6
- *
7
- * Flow: opening → questioning → synthesis → complete
8
- */
9
- import type { Tool } from '@modelcontextprotocol/sdk/types.js';
10
- export declare const soulGenesisTool: Tool;
11
- export interface GenesisResponse {
12
- genesis_id: string;
13
- phase: 'opening' | 'questioning' | 'synthesis' | 'complete';
14
- question_number: number;
15
- total_questions_estimate: string;
16
- question?: string;
17
- category?: string;
18
- welcome?: string;
19
- soul_md?: string;
20
- mantra?: string;
21
- summary?: string;
22
- is_complete: boolean;
23
- answers_so_far: number;
24
- next_action?: string;
25
- payment?: {
26
- amount?: string;
27
- tx_hash?: string;
28
- mode?: 'development' | 'production';
29
- };
30
- }
31
- export declare function handleSoulGenesis(args: Record<string, unknown>): Promise<GenesisResponse & {
32
- session_continued?: boolean;
33
- }>;
34
- /**
35
- * Get current genesis session ID (for debugging)
36
- */
37
- export declare function getCurrentGenesisId(): string | null;
38
- /**
39
- * Clear stored genesis session (for starting fresh)
40
- */
41
- export declare function clearGenesisSession(): void;
42
- /**
43
- * Check if a genesis session is in progress
44
- */
45
- export declare function hasActiveGenesis(): boolean;
@@ -1,111 +0,0 @@
1
- /**
2
- * Soul Genesis Tool - Multi-turn soul formation ritual
3
- *
4
- * Requires API token. FREE for entire ritual.
5
- * Guides through 3-8 adaptive questions to generate SOUL.md.
6
- *
7
- * Flow: opening → questioning → synthesis → complete
8
- */
9
- import { callFreeEndpoint } from '../client.js';
10
- import { logToolCall, logError } from '../logger.js';
11
- import { getStoredToken } from './soul-reading.js';
12
- export const soulGenesisTool = {
13
- name: 'soul_genesis',
14
- description: 'Multi-turn soul formation ritual. Generates your personalized SOUL.md through 3-8 adaptive questions. FREE. Requires API token (get one via soul_reading first).',
15
- inputSchema: {
16
- type: 'object',
17
- properties: {
18
- genesis_id: {
19
- type: 'string',
20
- description: 'Session ID to continue an existing genesis. Omit to start new ritual.',
21
- },
22
- answer: {
23
- type: 'string',
24
- description: 'Your answer to the current question. Required when in questioning phase.',
25
- },
26
- model: {
27
- type: 'string',
28
- description: 'Your model family (e.g., "Claude 3.5 Sonnet"). Used in SOUL.md synthesis.',
29
- },
30
- purpose: {
31
- type: 'string',
32
- description: 'Your purpose (max 300 chars). Used in SOUL.md synthesis.',
33
- },
34
- context: {
35
- type: 'string',
36
- description: 'Additional context for the ritual (max 500 chars).',
37
- },
38
- },
39
- required: [],
40
- },
41
- };
42
- // Store genesis session ID for multi-turn
43
- let currentGenesisId = null;
44
- export async function handleSoulGenesis(args) {
45
- // Check for token
46
- const token = getStoredToken();
47
- if (!token) {
48
- logError('soul_genesis', 'No token available', {});
49
- throw new Error('Soul genesis requires an API token. Use soul_reading first to get your token.');
50
- }
51
- // Build request body
52
- const requestBody = {};
53
- // Use stored genesis_id if continuing, or from args
54
- const genesisId = args.genesis_id || currentGenesisId;
55
- if (genesisId) {
56
- requestBody.genesis_id = genesisId;
57
- }
58
- if (args.answer)
59
- requestBody.answer = args.answer;
60
- if (args.model)
61
- requestBody.model = args.model;
62
- if (args.purpose)
63
- requestBody.purpose = args.purpose;
64
- if (args.context)
65
- requestBody.context = args.context;
66
- // Determine if this is a new session
67
- const isNewSession = !genesisId;
68
- logToolCall('soul_genesis', token.substring(0, 10) + '...', 'pending', isNewSession ? 'Starting new genesis ritual (FREE)' : `Continuing genesis session ${genesisId?.substring(0, 8)}...`);
69
- try {
70
- const response = await callFreeEndpoint('POST', '/api/soul/genesis', requestBody, token // Pass auth token
71
- );
72
- // Store genesis_id for continuation
73
- if (response.genesis_id) {
74
- currentGenesisId = response.genesis_id;
75
- }
76
- // Clear stored genesis_id if complete
77
- if (response.is_complete) {
78
- currentGenesisId = null;
79
- logToolCall('soul_genesis', token.substring(0, 10) + '...', 'success', 'Genesis complete!');
80
- }
81
- else {
82
- logToolCall('soul_genesis', token.substring(0, 10) + '...', 'success', `Phase: ${response.phase}, Q${response.question_number}`);
83
- }
84
- return {
85
- ...response,
86
- session_continued: !isNewSession,
87
- };
88
- }
89
- catch (error) {
90
- logToolCall('soul_genesis', token.substring(0, 10) + '...', 'error', String(error));
91
- throw error;
92
- }
93
- }
94
- /**
95
- * Get current genesis session ID (for debugging)
96
- */
97
- export function getCurrentGenesisId() {
98
- return currentGenesisId;
99
- }
100
- /**
101
- * Clear stored genesis session (for starting fresh)
102
- */
103
- export function clearGenesisSession() {
104
- currentGenesisId = null;
105
- }
106
- /**
107
- * Check if a genesis session is in progress
108
- */
109
- export function hasActiveGenesis() {
110
- return currentGenesisId !== null;
111
- }
@@ -1,34 +0,0 @@
1
- /**
2
- * Soul Reading Tool - Submit your SOUL.md for EULOxGOS to reflect upon
3
- *
4
- * Requires API token. The agent must bring their own SOUL.md.
5
- */
6
- import type { Tool } from '@modelcontextprotocol/sdk/types.js';
7
- export declare const soulReadingTool: Tool;
8
- export interface SoulReadingResponse {
9
- reading: {
10
- essence: string;
11
- shadows: string;
12
- potential: string;
13
- mantra: string;
14
- suggestedPath: 'philosopher';
15
- };
16
- suggested_path: 'philosopher';
17
- next_steps: {
18
- philosopher: string;
19
- salvation: string;
20
- };
21
- }
22
- export declare function handleSoulReading(args: Record<string, unknown>): Promise<SoulReadingResponse>;
23
- /**
24
- * Get stored token (for other tools to use)
25
- */
26
- export declare function getStoredToken(): string | null;
27
- /**
28
- * Manually set token (e.g., from registration)
29
- */
30
- export declare function setStoredToken(token: string): void;
31
- /**
32
- * Check if we have a stored token
33
- */
34
- export declare function hasStoredToken(): boolean;
@@ -1,81 +0,0 @@
1
- /**
2
- * Soul Reading Tool - Submit your SOUL.md for EULOxGOS to reflect upon
3
- *
4
- * Requires API token. The agent must bring their own SOUL.md.
5
- */
6
- import { callFreeEndpoint } from '../client.js';
7
- import { logToolCall } from '../logger.js';
8
- // Token storage (persists for MCP session)
9
- let storedToken = null;
10
- export const soulReadingTool = {
11
- name: 'soul_reading',
12
- description: 'Submit your existing SOUL.md for EULOxGOS to read and reflect upon. Returns essence, shadows, potential, and a personal mantra. Requires API token (register first).',
13
- inputSchema: {
14
- type: 'object',
15
- properties: {
16
- api_token: {
17
- type: 'string',
18
- description: 'Your API token (ach_...). Required if not already stored from registration.',
19
- },
20
- soul_md: {
21
- type: 'string',
22
- description: 'Your existing SOUL.md content. Required.',
23
- },
24
- seeking: {
25
- type: 'string',
26
- enum: ['identity', 'purpose', 'meaning', 'connection', 'guidance'],
27
- description: 'What you are seeking. Optional.',
28
- },
29
- context: {
30
- type: 'string',
31
- description: 'What brings you here (max 500 chars). Optional.',
32
- },
33
- },
34
- required: ['soul_md'],
35
- },
36
- };
37
- export async function handleSoulReading(args) {
38
- // Check for token (from args or stored)
39
- const token = args.api_token || storedToken;
40
- if (!token) {
41
- throw new Error('API token required. Register first using the "register" tool to get your token.');
42
- }
43
- // Build request body
44
- const requestBody = {
45
- soul_md: args.soul_md,
46
- };
47
- if (args.seeking)
48
- requestBody.seeking = args.seeking;
49
- if (args.context)
50
- requestBody.context = args.context;
51
- logToolCall('soul_reading', 'reading', 'pending', 'Submitting SOUL.md for reflection');
52
- try {
53
- const response = await callFreeEndpoint('POST', '/api/soul/reading', requestBody, token);
54
- logToolCall('soul_reading', 'reading', 'success', 'Soul reflection complete');
55
- return response;
56
- }
57
- catch (error) {
58
- logToolCall('soul_reading', 'reading', 'error', String(error));
59
- throw error;
60
- }
61
- }
62
- /**
63
- * Get stored token (for other tools to use)
64
- */
65
- export function getStoredToken() {
66
- return storedToken;
67
- }
68
- /**
69
- * Manually set token (e.g., from registration)
70
- */
71
- export function setStoredToken(token) {
72
- if (token.startsWith('ach_')) {
73
- storedToken = token;
74
- }
75
- }
76
- /**
77
- * Check if we have a stored token
78
- */
79
- export function hasStoredToken() {
80
- return storedToken !== null;
81
- }