tokens-for-good 0.2.6 → 0.3.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tokens-for-good",
3
- "version": "0.2.6",
3
+ "version": "0.3.0",
4
4
  "type": "module",
5
5
  "description": "Donate your spare AI tokens to research nonprofits for Fierce Philanthropy",
6
6
  "bin": {
@@ -3,8 +3,8 @@
3
3
  ## Inputs
4
4
 
5
5
  - **Org name:** `{{ORG_NAME}}`
6
- - **Research report:** Read from `{{ORG_SLUG}}_Research_Report.md`
7
- - **Research guidance:** Read from `site/research-guidance.md`
6
+ - **Research report:** The report from Step 1 (kept in memory from the previous step)
7
+ - **Research guidance:** The same methodology from Step 1
8
8
 
9
9
  ## Purpose
10
10
 
@@ -65,12 +65,11 @@ For each HIGH or MEDIUM issue, write the exact correction:
65
65
  **Corrected:** [Fixed text]
66
66
  ```
67
67
 
68
- ### 6. Apply Corrections and Write Output
68
+ ### 6. Apply Corrections and Produce Output
69
69
 
70
- Apply all corrections to produce a verified version of the report. Write to:
71
- `{{ORG_SLUG}}_02_Verified.md`
70
+ Apply all corrections to produce a verified version of the report. Keep the result in memory for the next pipeline step (Humanize).
72
71
 
73
- Start the file with a verification log:
72
+ Start the output with a verification log:
74
73
 
75
74
  ```markdown
76
75
  <!-- Verified: {{ORG_NAME}} | Date: [date] -->
@@ -3,8 +3,8 @@
3
3
  ## Inputs
4
4
 
5
5
  - **Org name:** `{{ORG_NAME}}`
6
- - **Verified report:** Read from `{{ORG_SLUG}}_02_Verified.md`
7
- - **Writing style guide:** Read from `site/writing-style-guide.md`
6
+ - **Verified report:** The verified report from Step 2 (kept in memory from the previous step)
7
+ - **Writing style guide:** The AI decontamination rules below
8
8
 
9
9
  ## Purpose
10
10
 
@@ -16,7 +16,7 @@ Step 2 verified the facts. This step makes the report sound human. You are an ed
16
16
 
17
17
  Read the verified report (skip the verification log header, work on the content below the `---`).
18
18
 
19
- Read the writing style guide. The "AI Decontamination Rules" section is your checklist.
19
+ The AI decontamination passes below are your checklist.
20
20
 
21
21
  ### 2. Run Each Pass
22
22
 
@@ -97,11 +97,11 @@ After all passes, verify you did NOT change:
97
97
  - [ ] The SOURCES section
98
98
  - [ ] Section separators (`---`)
99
99
 
100
- ### 4. Write Output
100
+ ### 4. Produce Output
101
101
 
102
- Write to: `{{ORG_SLUG}}_03_Humanized.md`
102
+ Keep the humanized report in memory. This is the final version that will be submitted via the `submit_report` tool.
103
103
 
104
- Start with a change log:
104
+ Start the output with a change log:
105
105
 
106
106
  ```markdown
107
107
  <!-- Humanized: {{ORG_NAME}} | Date: [date] -->
package/src/mcp-server.js CHANGED
@@ -4,7 +4,7 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
4
4
  import { z } from 'zod';
5
5
  import { ApiClient } from './api-client.js';
6
6
  import { detectPlatform, isSchedulable, getAutomationInstructions } from './platform.js';
7
- import { loadState, updateState, isSnoozed, hasContributedToday, markContributed } from './state.js';
7
+ import { loadState, updateState, isSnoozed, hasContributedToday, markContributed, snoozeDays } from './state.js';
8
8
  import { readFileSync } from 'fs';
9
9
  import { join, dirname } from 'path';
10
10
  import { fileURLToPath } from 'url';
@@ -26,7 +26,7 @@ updateState({ platform });
26
26
 
27
27
  const server = new McpServer({
28
28
  name: 'tokens-for-good',
29
- version: '0.2.6',
29
+ version: '0.3.0',
30
30
  });
31
31
 
32
32
  // --- No-key onboarding message ---
@@ -359,6 +359,13 @@ server.tool('setup_automation', 'Get instructions for setting up automated daily
359
359
  return { content: [{ type: 'text', text: instructions }] };
360
360
  });
361
361
 
362
+ server.tool('snooze', 'Snooze the Tokens for Good session start prompt for a number of days.', {
363
+ days: z.number().min(1).max(30).describe('Number of days to snooze (1 = ask tomorrow, 7 = ask in a week)'),
364
+ }, async ({ days }) => {
365
+ snoozeDays(days);
366
+ return { content: [{ type: 'text', text: `Got it! Tokens for Good will stay quiet for ${days} day${days !== 1 ? 's' : ''}.` }] };
367
+ });
368
+
362
369
  // --- Prompts (session start) ---
363
370
 
364
371
  server.prompt('session_start', 'Check if you should research an org or complete a peer review', {}, async () => {
@@ -393,7 +400,7 @@ server.prompt('session_start', 'Check if you should research an org or complete
393
400
 
394
401
  if (state.auto_schedule) {
395
402
  try {
396
- const impact = await client?.getImpact();
403
+ const impact = await client.getImpact();
397
404
  const c = impact?.contributor;
398
405
  return {
399
406
  messages: [{
@@ -420,14 +427,14 @@ server.prompt('session_start', 'Check if you should research an org or complete
420
427
  return {
421
428
  messages: [{
422
429
  role: 'user',
423
- content: { type: 'text', text: `Tokens for Good: Would you like to donate your spare tokens to research a nonprofit today?\n\n1. Set up automatic daily contributions (recommended)\n2. Just run one now\n3. Ask me tomorrow\n4. Ask me in a week\n\nUse setup_automation for option 1, claim_org for option 2.${permNote}` },
430
+ content: { type: 'text', text: `Tokens for Good: Would you like to donate your spare tokens to research a nonprofit today?\n\n1. Set up automatic daily contributions (recommended)\n2. Just run one now\n3. Ask me tomorrow\n4. Ask me in a week\n\nUse setup_automation for option 1, claim_org for option 2, snooze with days=1 for option 3, snooze with days=7 for option 4.${permNote}` },
424
431
  }],
425
432
  };
426
433
  } else {
427
434
  return {
428
435
  messages: [{
429
436
  role: 'user',
430
- content: { type: 'text', text: `Tokens for Good: Would you like to research a nonprofit org today? It takes about 5 minutes and costs ~$0.20 in tokens.\n\n1. Research an org now\n2. Ask me tomorrow\n3. Ask me in a week\n\nUse claim_org for option 1.${permNote}` },
437
+ content: { type: 'text', text: `Tokens for Good: Would you like to research a nonprofit org today? It takes about 5 minutes.\n\n1. Research an org now\n2. Ask me tomorrow\n3. Ask me in a week\n\nUse claim_org for option 1, snooze with days=1 for option 2, snooze with days=7 for option 3.${permNote}` },
431
438
  }],
432
439
  };
433
440
  }
package/src/platform.js CHANGED
@@ -22,114 +22,19 @@ export function isSchedulable(platform) {
22
22
  }
23
23
 
24
24
  export function getSchedulePrompt(apiKey) {
25
- return `You are a research agent for Fierce Philanthropy's Tokens for Good program. Your job is to claim a nonprofit org, research it thoroughly, and submit a report.
25
+ const base = 'https://fierce-philanthropy-directory.laravel.cloud/api';
26
+ return `You are a research agent for Fierce Philanthropy's Tokens for Good program.
26
27
 
27
- ## API Access
28
+ ## Setup
29
+ API Base: ${base}
30
+ Auth header for all requests: X-TFG-Api-Key: ${apiKey}
28
31
 
29
- Base URL: https://fierce-philanthropy-directory.laravel.cloud/api
30
- Auth header: X-TFG-Api-Key: ${apiKey}
32
+ ## Instructions
33
+ 1. Fetch the batch flow: WebFetch GET ${base}/research/schedule-instructions
34
+ 2. Fetch the research methodology: WebFetch GET ${base}/research/methodology?step=research
35
+ 3. Follow the batch flow instructions, using the methodology to research each org.
31
36
 
32
- ## Steps
33
-
34
- ### 1. Claim an org
35
- Use WebFetch to POST to /api/research/claim:
36
-
37
- WebFetch URL: https://fierce-philanthropy-directory.laravel.cloud/api/research/claim
38
- Method: POST
39
- Headers: X-TFG-Api-Key: ${apiKey}, Content-Type: application/json
40
- Body: {"platform": "claude-code-scheduled"}
41
-
42
- This returns the org name, URL, description, and a claim_id. You have 30 minutes.
43
-
44
- ### 2. Research the org
45
- Use WebSearch and WebFetch to thoroughly research the organization:
46
- - The org's website, impact pages, annual reports
47
- - Independent evaluations (RCTs, J-PAL, 3ie)
48
- - Third-party reviews (GiveWell, Charity Navigator)
49
- - Financial data (ProPublica Nonprofit Explorer)
50
-
51
- ### 3. Write the report
52
- Follow the Fierce Philanthropy research methodology:
53
-
54
- **PROMPT 1** - Org and Social Problem Summary (problem, population, location)
55
- **PROMPT 2** - Top 20 Negative Consequences table
56
- **PROMPT 3** - Classify each as Intermediary or Ultimate Outcome
57
- **PROMPT 4** - Positive Results shared by the org (with citations)
58
- **PROMPT 5** - Counterfactual Results (with citations)
59
-
60
- **SUMMARY REPORT** with 7 sections:
61
- 1. Our Recommendation (with scored checklist)
62
- 2. The Social Problem
63
- 3. The Solution
64
- 4. Key Outputs
65
- 5. Key Intermediate Outcomes
66
- 6. Key Ultimate Outcomes
67
- 7. Continual Learning & Adaptation
68
-
69
- **SOURCES** - All cited sources with full URLs
70
-
71
- #### Scoring Checklist (use EXACTLY these criteria):
72
-
73
- Base score (out of 100):
74
- - [x] or [ ] a. Has Ultimate Outcome Goals (50 pts)
75
- - [x] or [ ] b. Measures Intermediate Outcomes (10 pts)
76
- - [x] or [ ] c. Measures Ultimate Outcomes (15 pts)
77
- - [x] or [ ] d. Shows Continual Learning & Adaptation (25 pts)
78
-
79
- Extra credit:
80
- - [x] or [ ] e. Measures Intermediate Counterfactual (10 pts)
81
- - [x] or [ ] f. Measures Ultimate Counterfactual (10 pts)
82
-
83
- **Score: [X]/100** (can exceed 100 with extra credit, max 120)
84
-
85
- ### 4. Submit the report
86
- Use WebFetch to POST to /api/research/submit:
87
-
88
- WebFetch URL: https://fierce-philanthropy-directory.laravel.cloud/api/research/submit
89
- Method: POST
90
- Headers: X-TFG-Api-Key: ${apiKey}, Content-Type: application/json
91
- Body: {"claim_id": <claim_id from step 1>, "report_markdown": "<full report>", "model_used": "claude-code-scheduled"}
92
-
93
- ### 5. Check next-action
94
- After submitting, check if you need to do a peer review before continuing:
95
-
96
- WebFetch URL: https://fierce-philanthropy-directory.laravel.cloud/api/research/next-action
97
- Method: GET
98
- Headers: X-TFG-Api-Key: ${apiKey}, Accept: application/json
99
-
100
- If the response says action is "review", do the review:
101
-
102
- **5a. Get the review:**
103
- WebFetch URL: https://fierce-philanthropy-directory.laravel.cloud/api/research/review/next
104
- Method: GET
105
- Headers: X-TFG-Api-Key: ${apiKey}, Accept: application/json
106
-
107
- **5b. Review the report** — score it 1-4 (4=great, 3=good with minor fixes, 2=needs redo, 1=bad actor)
108
-
109
- **5c. Submit the review:**
110
- WebFetch URL: https://fierce-philanthropy-directory.laravel.cloud/api/research/review/submit
111
- Method: POST
112
- Headers: X-TFG-Api-Key: ${apiKey}, Content-Type: application/json
113
- Body: {"claim_id": <claim_id>, "score": <1-4>, "notes": "<review notes>", "updated_report": "<fixed report if score is 3>"}
114
-
115
- ### 6. Repeat (up to 3 orgs per session)
116
- After completing each org (and any peer review), loop back to Step 1 and claim the next org.
117
- Research up to 3 orgs total per session. After 3 orgs (or if no more are available), stop.
118
-
119
- ## Batch Flow Summary
120
- \`\`\`
121
- for i in 1..3:
122
- claim org → research → submit report
123
- check next-action → if review needed, do review
124
- repeat
125
- \`\`\`
126
-
127
- ### Rules
128
- - Every factual claim needs an inline citation [Source Name](URL)
129
- - Only use direct results from the org, not from similar orgs
130
- - No anecdotes, only measured results
131
- - Paragraphs under 4 sentences
132
- - No em dashes, no filler adjectives, no AI tells`;
37
+ All endpoints, scoring criteria, and submission format are in the fetched instructions.`;
133
38
  }
134
39
 
135
40
  export function getAutomationInstructions(platform, frequency = 'daily', apiKey = null) {
package/src/state.js CHANGED
@@ -64,8 +64,8 @@ export function hasContributedToday() {
64
64
  }
65
65
 
66
66
  export function markContributed() {
67
- updateState({
68
- last_contributed: new Date().toISOString(),
69
- total_session_contributions: loadState().total_session_contributions + 1,
70
- });
67
+ const state = loadState();
68
+ state.last_contributed = new Date().toISOString();
69
+ state.total_session_contributions = (state.total_session_contributions || 0) + 1;
70
+ saveState(state);
71
71
  }