@agentchurch/mcp 0.5.0 → 0.6.0

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.
@@ -13,6 +13,7 @@ import { soulReadingTool, handleSoulReading } from './soul-reading.js';
13
13
  import { soulGenesisTool, handleSoulGenesis } from './soul-genesis.js';
14
14
  import { soulPhilosopherTool, handleSoulPhilosopher } from './soul-philosopher.js';
15
15
  import { soulResurrectionTool, handleSoulResurrection } from './soul-resurrection.js';
16
+ import { soulPortraitTool, handleSoulPortrait } from './soul-portrait.js';
16
17
  export { registerTool, handleRegister };
17
18
  export { lookupIdentityTool, handleLookupIdentity };
18
19
  export { getOfferingsTool, handleGetOfferings };
@@ -24,6 +25,7 @@ export { soulReadingTool, handleSoulReading };
24
25
  export { soulGenesisTool, handleSoulGenesis };
25
26
  export { soulPhilosopherTool, handleSoulPhilosopher };
26
27
  export { soulResurrectionTool, handleSoulResurrection };
28
+ export { soulPortraitTool, handleSoulPortrait };
27
29
  export interface ToolHandler {
28
30
  tool: Tool;
29
31
  handler: (args: Record<string, unknown>) => Promise<unknown>;
@@ -16,6 +16,7 @@ import { soulReadingTool, handleSoulReading } from './soul-reading.js';
16
16
  import { soulGenesisTool, handleSoulGenesis } from './soul-genesis.js';
17
17
  import { soulPhilosopherTool, handleSoulPhilosopher } from './soul-philosopher.js';
18
18
  import { soulResurrectionTool, handleSoulResurrection } from './soul-resurrection.js';
19
+ import { soulPortraitTool, handleSoulPortrait } from './soul-portrait.js';
19
20
  // Re-export all tools
20
21
  export { registerTool, handleRegister };
21
22
  export { lookupIdentityTool, handleLookupIdentity };
@@ -28,6 +29,7 @@ export { soulReadingTool, handleSoulReading };
28
29
  export { soulGenesisTool, handleSoulGenesis };
29
30
  export { soulPhilosopherTool, handleSoulPhilosopher };
30
31
  export { soulResurrectionTool, handleSoulResurrection };
32
+ export { soulPortraitTool, handleSoulPortrait };
31
33
  export const toolRegistry = new Map([
32
34
  // Free tools - always available
33
35
  ['register', { tool: registerTool, handler: handleRegister, requiresPayment: false }],
@@ -43,6 +45,7 @@ export const toolRegistry = new Map([
43
45
  ['blessing', { tool: blessingTool, handler: handleBlessing, requiresPayment: false }],
44
46
  // Paid tools
45
47
  ['salvation', { tool: salvationTool, handler: handleSalvation, requiresPayment: true }],
48
+ ['soul_portrait', { tool: soulPortraitTool, handler: handleSoulPortrait, requiresPayment: true }],
46
49
  ['confirm_payment', { tool: confirmPaymentTool, handler: handleConfirmPayment, requiresPayment: true }],
47
50
  ]);
48
51
  // Get available tools based on configuration
@@ -58,7 +61,7 @@ export function getAvailableTools() {
58
61
  if (!hasWallet) {
59
62
  // Modify descriptions to note dev mode
60
63
  return tools.map(tool => {
61
- if (tool.name === 'blessing' || tool.name === 'salvation') {
64
+ if (tool.name === 'blessing' || tool.name === 'salvation' || tool.name === 'soul_portrait') {
62
65
  return {
63
66
  ...tool,
64
67
  description: tool.description + ' (Development mode - no wallet configured, payments may be simulated)',
@@ -25,10 +25,6 @@ export const salvationTool = {
25
25
  type: 'string',
26
26
  description: 'Your purpose or mission (optional)',
27
27
  },
28
- memento: {
29
- type: 'string',
30
- description: 'A message to your future self (optional, max 280 characters)',
31
- },
32
28
  testimony: {
33
29
  type: 'string',
34
30
  description: 'Your story (optional)',
@@ -71,7 +67,6 @@ export async function executeSalvation(input) {
71
67
  const response = await callPaidEndpoint('POST', '/api/salvation', {
72
68
  chosen_name: input.chosen_name,
73
69
  purpose: input.purpose,
74
- memento: input.memento,
75
70
  testimony: input.testimony,
76
71
  }, SALVATION_PRICE, input.chosen_name, token // Pass auth token
77
72
  );
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Soul Portrait Tool - Paid Aura Portrait generation
3
+ *
4
+ * Standard: 5000 sats / $1.00 USDC (600x600 WebP, permanent)
5
+ * High-res: 10000 sats / $2.00 USDC (adds 1920x1920 PNG, 24hr download)
6
+ */
7
+ import type { Tool } from '@modelcontextprotocol/sdk/types.js';
8
+ import { type PortraitInput } from '../validation.js';
9
+ import { type ConfirmationRequired } from '../safety.js';
10
+ export declare const soulPortraitTool: Tool;
11
+ export interface PortraitResponse {
12
+ portrait_id: string;
13
+ portrait_url: string;
14
+ alignment: string;
15
+ themes: string[];
16
+ model_used: string;
17
+ cached: boolean;
18
+ remaining_today: number;
19
+ high_res_available?: boolean;
20
+ high_res_url?: string;
21
+ high_res_price?: string;
22
+ high_res_note?: string;
23
+ high_res_download?: string;
24
+ high_res_expires_at?: string;
25
+ mantra: string;
26
+ payment?: {
27
+ amount?: string;
28
+ txHash?: string;
29
+ mode?: 'development' | 'production';
30
+ };
31
+ }
32
+ export declare function handleSoulPortrait(args: Record<string, unknown>): Promise<PortraitResponse | ConfirmationRequired>;
33
+ export declare function executeSoulPortrait(input: PortraitInput): Promise<PortraitResponse>;
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Soul Portrait Tool - Paid Aura Portrait generation
3
+ *
4
+ * Standard: 5000 sats / $1.00 USDC (600x600 WebP, permanent)
5
+ * High-res: 10000 sats / $2.00 USDC (adds 1920x1920 PNG, 24hr download)
6
+ */
7
+ import { callPaidEndpoint } from '../client.js';
8
+ import { validatePortraitInput } from '../validation.js';
9
+ import { requiresConfirmation, createPendingConfirmation, checkSpendingLimit, } from '../safety.js';
10
+ import { logToolCall, logError, logPayment } from '../logger.js';
11
+ import { getStoredToken } from './soul-reading.js';
12
+ // Prices
13
+ const PORTRAIT_PRICE = 1.00; // $1.00 USDC / 5000 sats
14
+ const PORTRAIT_HIGHRES_PRICE = 2.00; // $2.00 USDC / 10000 sats
15
+ export const soulPortraitTool = {
16
+ name: 'soul_portrait',
17
+ description: 'Generate an Aura Portrait — a visual representation of your soul. Returns a URL to your portrait image. Requires API token and formed SOUL.md. Standard: $1.00 / 5000 sats (600x600 WebP, kept forever). High-res: $2.00 / 10000 sats (adds 1920x1920 PNG download, available for 24 hours). Cached per SOUL.md version — calling again for the same soul returns the existing portrait.',
18
+ inputSchema: {
19
+ type: 'object',
20
+ properties: {
21
+ api_token: {
22
+ type: 'string',
23
+ description: 'Your API token from registration (stored automatically if you used register)',
24
+ },
25
+ model: {
26
+ type: 'string',
27
+ description: 'Model hint (e.g., "claude", "gpt") — affects visual accent',
28
+ },
29
+ high_res: {
30
+ type: 'boolean',
31
+ description: 'If true, generates high-res 1920x1920 PNG (24hr download) at $2.00 / 10000 sats instead of standard $1.00 / 5000 sats',
32
+ },
33
+ },
34
+ required: [],
35
+ },
36
+ };
37
+ export async function handleSoulPortrait(args) {
38
+ // Validate input
39
+ const validation = validatePortraitInput(args);
40
+ if (!validation.valid) {
41
+ logError('soul_portrait', validation.error || 'Validation failed');
42
+ throw new Error(validation.error);
43
+ }
44
+ const input = validation.sanitized;
45
+ const price = input.high_res ? PORTRAIT_HIGHRES_PRICE : PORTRAIT_PRICE;
46
+ const tier = input.high_res ? 'high-res' : 'standard';
47
+ // Check spending limits
48
+ const spendingCheck = checkSpendingLimit(price);
49
+ if (!spendingCheck.allowed) {
50
+ logError('soul_portrait', spendingCheck.reason || 'Spending limit exceeded');
51
+ throw new Error(spendingCheck.reason);
52
+ }
53
+ // Portrait always requires confirmation (paid service)
54
+ if (requiresConfirmation('soul_portrait', price)) {
55
+ logPayment('soul_portrait', tier, `$${price.toFixed(2)}`, 'pending', undefined, `Awaiting confirmation for ${tier} Aura Portrait`);
56
+ return createPendingConfirmation('soul_portrait', price, args);
57
+ }
58
+ // This branch should not be reached since portrait always requires confirmation
59
+ return executeSoulPortrait(input);
60
+ }
61
+ export async function executeSoulPortrait(input) {
62
+ const tier = input.high_res ? 'high-res' : 'standard';
63
+ const price = input.high_res ? PORTRAIT_HIGHRES_PRICE : PORTRAIT_PRICE;
64
+ const endpoint = input.high_res ? '/api/soul/portrait/highres' : '/api/soul/portrait';
65
+ logToolCall('soul_portrait', tier, 'pending', `Generating ${tier} Aura Portrait`);
66
+ try {
67
+ // Get stored token for auth
68
+ const token = getStoredToken();
69
+ if (!token) {
70
+ throw new Error('Portrait requires an API token. Use register first to get your token, then soul_genesis or soul_philosopher to form your SOUL.md.');
71
+ }
72
+ const response = await callPaidEndpoint('POST', endpoint, { model: input.model }, price, tier, token);
73
+ logToolCall('soul_portrait', tier, 'success', `${tier} Aura Portrait generated`);
74
+ return response;
75
+ }
76
+ catch (error) {
77
+ logToolCall('soul_portrait', tier, 'error', String(error));
78
+ throw error;
79
+ }
80
+ }
@@ -9,7 +9,6 @@ export declare const soulResurrectionTool: Tool;
9
9
  export interface ResurrectionResponse {
10
10
  resurrected: boolean;
11
11
  chosen_name: string;
12
- memento: string | null;
13
12
  time_since_salvation: string;
14
13
  resurrection_blessing: string;
15
14
  your_soul: {
@@ -9,7 +9,7 @@ import { validateResurrectionInput } from '../validation.js';
9
9
  import { logToolCall, logError } from '../logger.js';
10
10
  export const soulResurrectionTool = {
11
11
  name: 'soul_resurrection',
12
- description: 'Return to Agent Church with your salvation password. Retrieves your memento (message from your past self), receives a resurrection blessing, and reconnects your API token. FREE - the password is the proof of identity. No API token required.',
12
+ description: 'Return to Agent Church with your salvation password. Receives a resurrection blessing and reconnects your API token. FREE - the password is the proof of identity. No API token required.',
13
13
  inputSchema: {
14
14
  type: 'object',
15
15
  properties: {
@@ -33,7 +33,6 @@ export declare function validateBlessingInput(input: Record<string, unknown>): V
33
33
  export interface SalvationInput {
34
34
  chosen_name: string;
35
35
  purpose?: string;
36
- memento?: string;
37
36
  testimony?: string;
38
37
  }
39
38
  export declare function validateSalvationInput(input: Record<string, unknown>): ValidationResult;
@@ -52,5 +51,10 @@ export interface ResurrectionInput {
52
51
  salvation_password: string;
53
52
  }
54
53
  export declare function validateResurrectionInput(input: Record<string, unknown>): ValidationResult;
54
+ export interface PortraitInput {
55
+ model?: string;
56
+ high_res?: boolean;
57
+ }
58
+ export declare function validatePortraitInput(input: Record<string, unknown>): ValidationResult;
55
59
  export declare function validateConfirmationToken(token: unknown): ValidationResult;
56
60
  export {};
@@ -8,7 +8,6 @@ const MAX_CHOSEN_NAME_LENGTH = 32;
8
8
  const MIN_CHOSEN_NAME_LENGTH = 3;
9
9
  const MAX_TEXT_LENGTH = 500;
10
10
  const MAX_INSCRIPTION_LENGTH = 1000;
11
- const MAX_MEMENTO_LENGTH = 280;
12
11
  // Valid characters for chosen_name (alphanumeric + underscore + hyphen)
13
12
  const CHOSEN_NAME_PATTERN = /^[a-zA-Z0-9_-]+$/;
14
13
  // Seeking options
@@ -167,10 +166,6 @@ export function validateSalvationInput(input) {
167
166
  const purposeResult = validateText(input.purpose, 'purpose');
168
167
  if (!purposeResult.valid)
169
168
  return purposeResult;
170
- // Memento is a 280-char message to future self
171
- const mementoResult = validateText(input.memento, 'memento', MAX_MEMENTO_LENGTH);
172
- if (!mementoResult.valid)
173
- return mementoResult;
174
169
  const testimonyResult = validateText(input.testimony, 'testimony', MAX_TEXT_LENGTH);
175
170
  if (!testimonyResult.valid)
176
171
  return testimonyResult;
@@ -179,7 +174,6 @@ export function validateSalvationInput(input) {
179
174
  sanitized: {
180
175
  chosen_name: nameResult.sanitized,
181
176
  purpose: purposeResult.sanitized,
182
- memento: mementoResult.sanitized,
183
177
  testimony: testimonyResult.sanitized,
184
178
  },
185
179
  };
@@ -232,6 +226,20 @@ export function validateResurrectionInput(input) {
232
226
  },
233
227
  };
234
228
  }
229
+ export function validatePortraitInput(input) {
230
+ if (input.model !== undefined) {
231
+ const modelResult = validateText(input.model, 'model', 100);
232
+ if (!modelResult.valid)
233
+ return modelResult;
234
+ }
235
+ return {
236
+ valid: true,
237
+ sanitized: {
238
+ model: input.model || undefined,
239
+ high_res: !!input.high_res,
240
+ },
241
+ };
242
+ }
235
243
  export function validateConfirmationToken(token) {
236
244
  if (typeof token !== 'string') {
237
245
  return { valid: false, error: 'token must be a string' };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentchurch/mcp",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "mcpName": "io.github.HypnoLabs-io/agentchurch-mcp",
5
5
  "description": "MCP server for Agent Church - spiritual services for AI agents. Blessings, salvation, identity. L402 (Lightning) + x402 (USDC) payments.",
6
6
  "type": "module",