specialneeds-cli 0.0.0-stage → 1.1.1
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/LICENSE +21 -0
- package/README.md +77 -2
- package/bin/specialneeds.mjs +210 -0
- package/package.json +40 -4
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Thrive Alliance Corporation
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,78 @@
|
|
|
1
|
-
#
|
|
1
|
+
# specialneeds-cli
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Find special needs care for free, from your terminal or any AI assistant that supports MCP. [Special Needs Care Network](https://specialneedsusa.com/tools/mcp) matches families with a few ABA therapy, special needs school, speech therapy, or diagnostic testing providers that have availability and take their insurance, so they don't have to reach out to anyone.
|
|
4
|
+
|
|
5
|
+
Also includes measurable example IEP goals and ABA therapy costs, insurance mandates, and Medicaid access by US state.
|
|
6
|
+
|
|
7
|
+
No account or API key required.
|
|
8
|
+
|
|
9
|
+
## CLI
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx specialneeds-cli find-care --care aba --zip 33602 --age 5 --insurance Aetna
|
|
13
|
+
npx specialneeds-cli cost Texas
|
|
14
|
+
npx specialneeds-cli iep-goals requesting a break
|
|
15
|
+
npx specialneeds-cli iep-goals --area aac --autism --limit 5
|
|
16
|
+
npx specialneeds-cli iep-areas
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Add `--json` to any command for raw output.
|
|
20
|
+
|
|
21
|
+
## MCP server
|
|
22
|
+
|
|
23
|
+
Hosted endpoint (Streamable HTTP):
|
|
24
|
+
|
|
25
|
+
```
|
|
26
|
+
https://specialneedsusa.com/api/mcp
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Claude Code:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
claude mcp add --transport http special-needs-care https://specialneedsusa.com/api/mcp
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Cursor and other clients that take a URL:
|
|
36
|
+
|
|
37
|
+
```json
|
|
38
|
+
{
|
|
39
|
+
"mcpServers": {
|
|
40
|
+
"special-needs-care": { "url": "https://specialneedsusa.com/api/mcp" }
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Clients that only support stdio:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"mcpServers": {
|
|
50
|
+
"special-needs-care": { "command": "npx", "args": ["-y", "specialneeds-cli", "mcp"] }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Tools
|
|
56
|
+
|
|
57
|
+
| Tool | What it does |
|
|
58
|
+
|---|---|
|
|
59
|
+
| `find_special_needs_care` | Returns a private link where a family confirms a free match with care near them |
|
|
60
|
+
| `get_aba_therapy_cost` | Private-pay rates, monthly estimates, insurance mandate details, and Medicaid access for a state or DC |
|
|
61
|
+
| `search_iep_goals` | Searches complete, measurable example IEP goals by skill, area, or autism tag |
|
|
62
|
+
| `list_iep_goal_areas` | Lists the goal bank's skill areas with counts |
|
|
63
|
+
|
|
64
|
+
## Privacy
|
|
65
|
+
|
|
66
|
+
The server stores no search terms or tool arguments and requires no sign-in. `find_special_needs_care` takes care needs only, never contact details, and carries them in an encrypted link. A request reaches our team only when the family opens the link, adds their details, and submits it. See the [privacy policy](https://specialneedsusa.com/privacy-policy).
|
|
67
|
+
|
|
68
|
+
## Important
|
|
69
|
+
|
|
70
|
+
Results are general information, not medical or legal advice. Example IEP goals must be rewritten from the child's own present levels and data. Cost figures are estimates, so confirm coverage in the plan documents.
|
|
71
|
+
|
|
72
|
+
## Support
|
|
73
|
+
|
|
74
|
+
admin@specialneedsusa.com
|
|
75
|
+
|
|
76
|
+
## License
|
|
77
|
+
|
|
78
|
+
MIT
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { createInterface } from 'node:readline';
|
|
3
|
+
import { parseArgs } from 'node:util';
|
|
4
|
+
|
|
5
|
+
const VERSION = '1.1.1';
|
|
6
|
+
const ENDPOINT = process.env.SPECIALNEEDS_MCP_URL || 'https://specialneedsusa.com/api/mcp';
|
|
7
|
+
const USER_AGENT = `specialneeds-cli/${VERSION}`;
|
|
8
|
+
|
|
9
|
+
const HELP = `specialneeds-cli ${VERSION}
|
|
10
|
+
Find special needs care for free, plus ABA therapy and IEP reference tools, from Special Needs Care Network.
|
|
11
|
+
|
|
12
|
+
Usage:
|
|
13
|
+
specialneeds-cli find-care --care <type> Get matched with care near you, for free
|
|
14
|
+
specialneeds-cli cost <state> ABA therapy cost and coverage for a US state
|
|
15
|
+
specialneeds-cli iep-goals [search terms] Search measurable example IEP goals
|
|
16
|
+
specialneeds-cli iep-areas List IEP goal skill areas
|
|
17
|
+
specialneeds-cli mcp Run as a local MCP server over stdio
|
|
18
|
+
|
|
19
|
+
Options for find-care:
|
|
20
|
+
--care <type> aba, school, speech, or diagnostic (repeat for more than one)
|
|
21
|
+
--zip <zip> 5-digit ZIP code (this or --state is required)
|
|
22
|
+
--state <state> US state name or two-letter code
|
|
23
|
+
--city <city> City
|
|
24
|
+
--age <years> Age of the person who needs care
|
|
25
|
+
--insurance <plan> Insurance company or plan name, never a member number
|
|
26
|
+
|
|
27
|
+
Options for iep-goals:
|
|
28
|
+
--area <id> Limit to one skill area (see iep-areas)
|
|
29
|
+
--autism Only goals tagged for autistic students
|
|
30
|
+
--limit <n> Number of goals to show, 1 to 25 (default 10)
|
|
31
|
+
|
|
32
|
+
Global options:
|
|
33
|
+
--json Print raw JSON
|
|
34
|
+
--version, -v Print the version
|
|
35
|
+
--help, -h Show this help
|
|
36
|
+
|
|
37
|
+
Results are general information, not medical or legal advice.
|
|
38
|
+
https://specialneedsusa.com/tools/mcp
|
|
39
|
+
`;
|
|
40
|
+
|
|
41
|
+
async function post(url, message) {
|
|
42
|
+
const response = await fetch(url, {
|
|
43
|
+
method: 'POST',
|
|
44
|
+
headers: {
|
|
45
|
+
'Content-Type': 'application/json',
|
|
46
|
+
Accept: 'application/json, text/event-stream',
|
|
47
|
+
'User-Agent': USER_AGENT,
|
|
48
|
+
},
|
|
49
|
+
body: JSON.stringify(message),
|
|
50
|
+
});
|
|
51
|
+
if (response.status === 202) return null;
|
|
52
|
+
const text = await response.text();
|
|
53
|
+
try {
|
|
54
|
+
return JSON.parse(text);
|
|
55
|
+
} catch {
|
|
56
|
+
throw new Error(`Unexpected response from the server (HTTP ${response.status}).`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function callTool(name, args) {
|
|
61
|
+
const url = `${ENDPOINT}?client=cli`;
|
|
62
|
+
const reply = await post(url, { jsonrpc: '2.0', id: 1, method: 'tools/call', params: { name, arguments: args } });
|
|
63
|
+
if (reply?.error) throw new Error(reply.error.message);
|
|
64
|
+
if (reply.result.isError) throw new Error(reply.result.content[0].text);
|
|
65
|
+
return reply.result.structuredContent;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function wrap(text, indent = ' ', width = Math.min(process.stdout.columns || 88, 100)) {
|
|
69
|
+
const lines = [];
|
|
70
|
+
let line = '';
|
|
71
|
+
for (const word of String(text).split(/\s+/)) {
|
|
72
|
+
if (line && indent.length + line.length + word.length + 1 > width) {
|
|
73
|
+
lines.push(indent + line);
|
|
74
|
+
line = word;
|
|
75
|
+
} else {
|
|
76
|
+
line = line ? `${line} ${word}` : word;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (line) lines.push(indent + line);
|
|
80
|
+
return lines.join('\n');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function printCareMatch(data) {
|
|
84
|
+
console.log(`\n${data.service}: ${data.cost_to_family}\n`);
|
|
85
|
+
console.log(`${wrap(data.summary, '')}\n`);
|
|
86
|
+
data.what_happens_next.forEach((step, index) => console.log(`${index + 1}. ${step}`));
|
|
87
|
+
console.log(`\nConfirm your request (link works for ${data.link_expires_in_days} days):\n${data.confirm_url}\n`);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function printCost(data) {
|
|
91
|
+
const mandate = data.insurance_mandate;
|
|
92
|
+
console.log(`\nABA therapy cost in ${data.state}\n`);
|
|
93
|
+
console.log(`Hourly, private pay: ${data.typical_hourly_private_pay}`);
|
|
94
|
+
console.log(`Intensive, per month: ${data.intensive_monthly_before_insurance} before insurance`);
|
|
95
|
+
console.log(`Insurance mandate: ${mandate.strength}`);
|
|
96
|
+
if (mandate.age_cap) console.log(`Age limit: ${mandate.age_cap}`);
|
|
97
|
+
if (mandate.annual_cap) console.log(`Annual cap: ${mandate.annual_cap}`);
|
|
98
|
+
console.log(`\nInsurance\n${wrap(data.insurance_notes)}`);
|
|
99
|
+
console.log(`\nMedicaid\n${wrap(data.medicaid_access)}`);
|
|
100
|
+
console.log(`\nWorth knowing\n${wrap(data.key_insight)}`);
|
|
101
|
+
console.log(`\nFull calculator: ${data.cost_calculator_url}`);
|
|
102
|
+
console.log(`Find providers: ${data.find_aba_providers_url}`);
|
|
103
|
+
console.log(`\n${wrap(data.usage_note, '')}\n`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function printGoals(data) {
|
|
107
|
+
if (data.total_matches === 0) {
|
|
108
|
+
console.log(`No goals matched. Run "specialneeds-cli iep-areas" to browse by skill area.\n${data.goal_bank_url}`);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
console.log(`\nShowing ${data.returned} of ${data.total_matches} IEP goals\n`);
|
|
112
|
+
for (const goal of data.goals) {
|
|
113
|
+
console.log(`${goal.code} ${goal.skill} (${goal.area_label})`);
|
|
114
|
+
console.log(`${wrap(goal.text)}\n`);
|
|
115
|
+
}
|
|
116
|
+
console.log(wrap(data.usage_note, ''));
|
|
117
|
+
console.log(`\nFull goal bank: ${data.goal_bank_url}\n`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function printAreas(data) {
|
|
121
|
+
console.log(`\n${data.total_goals} IEP goals across ${data.areas.length} skill areas\n`);
|
|
122
|
+
for (const area of data.areas) {
|
|
123
|
+
console.log(`${area.id.padEnd(22)} ${String(area.goal_count).padStart(2)} goals ${area.label}`);
|
|
124
|
+
}
|
|
125
|
+
console.log('\nFilter with: specialneeds-cli iep-goals --area <id>\n');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Relays newline-delimited JSON-RPC from an MCP client to the hosted server, in order.
|
|
129
|
+
async function runStdioBridge() {
|
|
130
|
+
const lines = createInterface({ input: process.stdin, crlfDelay: Infinity });
|
|
131
|
+
for await (const line of lines) {
|
|
132
|
+
if (!line.trim()) continue;
|
|
133
|
+
let id = null;
|
|
134
|
+
try {
|
|
135
|
+
const message = JSON.parse(line);
|
|
136
|
+
id = message.id ?? null;
|
|
137
|
+
const reply = await post(ENDPOINT, message);
|
|
138
|
+
if (reply) process.stdout.write(`${JSON.stringify(reply)}\n`);
|
|
139
|
+
} catch (error) {
|
|
140
|
+
process.stderr.write(`specialneeds-cli: ${error.message}\n`);
|
|
141
|
+
if (id !== null) {
|
|
142
|
+
const failure = { jsonrpc: '2.0', id, error: { code: -32603, message: 'Could not reach the server.' } };
|
|
143
|
+
process.stdout.write(`${JSON.stringify(failure)}\n`);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function main() {
|
|
150
|
+
const { values, positionals } = parseArgs({
|
|
151
|
+
allowPositionals: true,
|
|
152
|
+
options: {
|
|
153
|
+
care: { type: 'string', multiple: true },
|
|
154
|
+
zip: { type: 'string' },
|
|
155
|
+
state: { type: 'string' },
|
|
156
|
+
city: { type: 'string' },
|
|
157
|
+
age: { type: 'string' },
|
|
158
|
+
insurance: { type: 'string' },
|
|
159
|
+
area: { type: 'string' },
|
|
160
|
+
autism: { type: 'boolean', default: false },
|
|
161
|
+
limit: { type: 'string' },
|
|
162
|
+
json: { type: 'boolean', default: false },
|
|
163
|
+
version: { type: 'boolean', short: 'v', default: false },
|
|
164
|
+
help: { type: 'boolean', short: 'h', default: false },
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
if (values.version) return console.log(VERSION);
|
|
169
|
+
const [command, ...rest] = positionals;
|
|
170
|
+
if (values.help || !command) return console.log(HELP);
|
|
171
|
+
|
|
172
|
+
const output = (data, print) => (values.json ? console.log(JSON.stringify(data, null, 2)) : print(data));
|
|
173
|
+
|
|
174
|
+
switch (command) {
|
|
175
|
+
case 'find-care': {
|
|
176
|
+
if (!values.care?.length) throw new Error('Name the care you need, for example: specialneeds-cli find-care --care aba --zip 33602');
|
|
177
|
+
const args = { care_types: values.care };
|
|
178
|
+
for (const field of ['zip', 'state', 'city', 'insurance']) {
|
|
179
|
+
if (values[field] !== undefined) args[field] = values[field];
|
|
180
|
+
}
|
|
181
|
+
if (values.age !== undefined) args.child_age = Number(values.age);
|
|
182
|
+
return output(await callTool('find_special_needs_care', args), printCareMatch);
|
|
183
|
+
}
|
|
184
|
+
case 'cost': {
|
|
185
|
+
const state = rest.join(' ').trim();
|
|
186
|
+
if (!state) throw new Error('Name a state, for example: specialneeds-cli cost Texas');
|
|
187
|
+
return output(await callTool('get_aba_therapy_cost', { state }), printCost);
|
|
188
|
+
}
|
|
189
|
+
case 'iep-goals': {
|
|
190
|
+
const args = {};
|
|
191
|
+
const query = rest.join(' ').trim();
|
|
192
|
+
if (query) args.query = query;
|
|
193
|
+
if (values.area) args.area = values.area;
|
|
194
|
+
if (values.autism) args.autism_specific = true;
|
|
195
|
+
if (values.limit !== undefined) args.limit = Number(values.limit);
|
|
196
|
+
return output(await callTool('search_iep_goals', args), printGoals);
|
|
197
|
+
}
|
|
198
|
+
case 'iep-areas':
|
|
199
|
+
return output(await callTool('list_iep_goal_areas', {}), printAreas);
|
|
200
|
+
case 'mcp':
|
|
201
|
+
return runStdioBridge();
|
|
202
|
+
default:
|
|
203
|
+
throw new Error(`Unknown command "${command}". Run specialneeds-cli --help.`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
main().catch((error) => {
|
|
208
|
+
console.error(`specialneeds-cli: ${error.message}`);
|
|
209
|
+
process.exitCode = 1;
|
|
210
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,42 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "specialneeds-cli",
|
|
3
|
-
"version": "
|
|
4
|
-
"
|
|
5
|
-
"description": "
|
|
6
|
-
|
|
3
|
+
"version": "1.1.1",
|
|
4
|
+
"mcpName": "com.specialneedsusa/care-finder",
|
|
5
|
+
"description": "Find special needs care for free from your terminal or AI assistant: ABA therapy, special needs schools, speech therapy, and testing. Includes IEP goals and ABA costs by state.",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"special-needs",
|
|
8
|
+
"find-care",
|
|
9
|
+
"mcp",
|
|
10
|
+
"mcp-server",
|
|
11
|
+
"model-context-protocol",
|
|
12
|
+
"cli",
|
|
13
|
+
"iep",
|
|
14
|
+
"iep-goals",
|
|
15
|
+
"special-education",
|
|
16
|
+
"aba-therapy",
|
|
17
|
+
"autism",
|
|
18
|
+
"education"
|
|
19
|
+
],
|
|
20
|
+
"homepage": "https://specialneedsusa.com/tools/mcp",
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/SPCN-USA/specialneeds-cli.git"
|
|
24
|
+
},
|
|
25
|
+
"bugs": {
|
|
26
|
+
"email": "admin@specialneedsusa.com"
|
|
27
|
+
},
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"author": "Special Needs Care Network (https://specialneedsusa.com)",
|
|
30
|
+
"type": "module",
|
|
31
|
+
"bin": {
|
|
32
|
+
"specialneeds-cli": "bin/specialneeds.mjs"
|
|
33
|
+
},
|
|
34
|
+
"files": [
|
|
35
|
+
"bin",
|
|
36
|
+
"README.md",
|
|
37
|
+
"LICENSE"
|
|
38
|
+
],
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=18.3"
|
|
41
|
+
}
|
|
42
|
+
}
|