tascan-mcp 3.14.1 → 3.15.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/README.md +6 -0
- package/index.js +1 -1
- package/package.json +1 -1
- package/scopes.cjs +3 -0
- package/tools.cjs +45 -4
package/README.md
CHANGED
|
@@ -268,6 +268,12 @@ API keys are scoped to your organization and support rate limiting (60 requests/
|
|
|
268
268
|
|
|
269
269
|
## Changelog
|
|
270
270
|
|
|
271
|
+
### v3.15.0 — 2026-09-19
|
|
272
|
+
|
|
273
|
+
- `tascan_create_cycle` gains an optional `reviews[]` array — a multi-lens review panel (design item 14a): 1–8 lenses, each `{lens, brief, provider: openai|anthropic|gemini, model?, blocking? (default true), max_tool_calls? (openai only)}`, forwarded to `POST /coord/cycles`; at least one lens must be blocking. Omit it for the single OpenAI review.
|
|
274
|
+
- `tascan_get_cycle_report` prints `review[<lens>]` for panel review steps; legacy steps print `review` unchanged.
|
|
275
|
+
- 79 tools, no new tool, scopes unchanged.
|
|
276
|
+
|
|
271
277
|
### v3.14.1 — 2026-09-18
|
|
272
278
|
- `tascan_create_cycle`: new optional `repo` argument — which codebase on the executor the build runs in, an alias from the executor's allowlist (`tascan-agent/repos.json`; default `tascan`). `artifact_paths` are relative to that repo. An unknown alias is refused by the executor (`repo_unknown`) and the cycle parks — the protocol can now build, review and gate work in any project the executor lists, not only TaScan itself (migration 161).
|
|
273
279
|
- 78 tools.
|
package/index.js
CHANGED
|
@@ -46,7 +46,7 @@ async function api(method, path, body) {
|
|
|
46
46
|
api.key = API_KEY; // raw-fetch tools read the key from here (S99: those functions now require auth)
|
|
47
47
|
|
|
48
48
|
const server = new Server(
|
|
49
|
-
{ name: 'tascan', version: '3.
|
|
49
|
+
{ name: 'tascan', version: '3.15.0' },
|
|
50
50
|
{ capabilities: { tools: {} } }
|
|
51
51
|
);
|
|
52
52
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tascan-mcp",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.15.0",
|
|
4
4
|
"mcpName": "io.github.snowbikemike/tascan-mcp",
|
|
5
5
|
"description": "TaScan MCP Server — Closed-loop autonomous operations protocol. 70 tools for projects, tasks, workers, QR codes, NFC tags, worker marketplace with consented SMS invites, geofenced zones with rules (work site / hazard + AI-verified PPE checkpoints / containment / restricted, breach alerts, zone compliance audit), AI condition assessment, worker passports, verification-gated gig payments, client invoicing from verified work (overtime / day rate / per diem / expenses), shareable report links (completion, client service report, project, evidence pack), signed Action Receipts (Ed25519 JWS), cross-entity search, duplicate-worker detection, worker identity merge, AI issue analysis, and autonomous remediation dispatch. 15 patents filed (470+ claims). Task. Scan. Done.",
|
|
6
6
|
"type": "module",
|
package/scopes.cjs
CHANGED
|
@@ -179,6 +179,8 @@ const TOOL_SCOPES = {
|
|
|
179
179
|
tascan_get_receipt: R, // W3 receipt.js (GET /api/v1/receipts/:id — read tier there too)
|
|
180
180
|
// coordination layer (protocol v0.2 design item 1, migration 155) — read tier: the reviewer's ['read'] key may call these
|
|
181
181
|
tascan_get_build: R, tascan_get_build_file: R, tascan_get_cycle_report: R,
|
|
182
|
+
// design item 13c: one call that summarizes a whole project for a chat client
|
|
183
|
+
tascan_project_digest: R,
|
|
182
184
|
// write here; REST upgrades POST /tasks/:id/messages to agent:dispatch (:code for CODE/SHELL askers) when the task is a cycle task
|
|
183
185
|
tascan_post_message: W,
|
|
184
186
|
// creating a cycle queues a CODE:/SHELL: prompt on the AI Inbox → agent:dispatch:code, always
|
|
@@ -228,6 +230,7 @@ const ROUTE_SCOPES = {
|
|
|
228
230
|
project: { GET: R, PUT: W, DELETE: W },
|
|
229
231
|
project_lists: { GET: R, POST: W },
|
|
230
232
|
project_responses: { GET: R },
|
|
233
|
+
project_digest: { GET: R }, // design item 13c: GET /projects/:id/digest
|
|
231
234
|
list: { GET: R, PUT: W, DELETE: W },
|
|
232
235
|
list_tasks: { GET: R, POST: INBOX_WRITE },
|
|
233
236
|
list_qr: { POST: W },
|
package/tools.cjs
CHANGED
|
@@ -1992,14 +1992,14 @@ const TOOLS = [
|
|
|
1992
1992
|
// SECURITY DEFINER RPCs behind human completions. The cycle report is the audit surface.
|
|
1993
1993
|
{
|
|
1994
1994
|
name: 'tascan_create_cycle',
|
|
1995
|
-
description: 'Start an unattended build-review-decide cycle (protocol v0.2). Queues T1 CODE: (or SHELL:) with your build_brief on the AI Inbox and T2 REVIEW: with your review_brief, born blocked on T1. The local executor builds, stores the exact bytes of artifact_paths as a bundle (build_ref = sha256 over the manifest), the independent reviewer reviews THAT bundle, an approve verdict mints a Decision task for the human authority (one SMS), Approve mints an Integrate task for the deploy id. Revise verdicts spawn revisions (cap max_revisions, default 3); reject, human Reject, scope violations or exhausted revisions PARK the cycle (a Parked task with Resume with notes / Close). REQUIRES agent:dispatch:code. Duplicate protection: the same idempotency_key, or the same briefs + paths, within 24 h returns the existing root instead of queueing again (created=false). Track with tascan_get_cycle_report (root_id). Nothing spawns a cycle on its own.',
|
|
1995
|
+
description: 'Start an unattended build-review-decide cycle (protocol v0.2). Queues T1 CODE: (or SHELL:) with your build_brief on the AI Inbox and T2 REVIEW: with your review_brief, born blocked on T1. The local executor builds, stores the exact bytes of artifact_paths as a bundle (build_ref = sha256 over the manifest), the independent reviewer reviews THAT bundle, an approve verdict mints a Decision task for the human authority (one SMS), Approve mints an Integrate task for the deploy id. Revise verdicts spawn revisions (cap max_revisions, default 3); reject, human Reject, scope violations or exhausted revisions PARK the cycle (a Parked task with Resume with notes / Close). REQUIRES agent:dispatch:code. Duplicate protection: the same idempotency_key, or the same briefs + paths, within 24 h returns the existing root instead of queueing again (created=false). Optional `reviews[]` attaches a multi-lens review panel (design item 14a) in place of the single OpenAI review — one review task mints per lens and every blocking lens must approve before the Decision task mints. Track with tascan_get_cycle_report (root_id). Nothing spawns a cycle on its own.',
|
|
1996
1996
|
inputSchema: {
|
|
1997
1997
|
type: 'object',
|
|
1998
1998
|
properties: {
|
|
1999
1999
|
project_id: { type: 'string', description: 'Working project (UUID). Its Decisions and Agent Questions lists are created on the first cycle (coord_ensure_lists). Requires the org human authority to be configured (coord_set_authority) — otherwise 403.' },
|
|
2000
2000
|
title: { type: 'string', description: 'Short human title (1-200 chars). T1 becomes "CODE: <title>", T2 "REVIEW: <title>".' },
|
|
2001
2001
|
build_brief: { type: 'string', description: 'The executor prompt (1-40000 chars). Executed verbatim by the local Claude Code runner as a CODE:/SHELL: task — write it as a complete instruction, name the files, forbid nothing the runner already forbids (deploy/commit/push are refused by its context).' },
|
|
2002
|
-
review_brief: { type: 'string', description: 'The reviewer prompt (1-40000 chars). The reviewer reads the stored bundle (tascan_get_build / tascan_get_build_file), the task text and the filtered trail; it returns approve, revise, reject or needs_input as strict JSON bound to the build_ref.' },
|
|
2002
|
+
review_brief: { type: 'string', description: 'The reviewer prompt (1-40000 chars). The reviewer reads the stored bundle (tascan_get_build / tascan_get_build_file), the task text and the filtered trail; it returns approve, revise, reject or needs_input as strict JSON bound to the build_ref. Still required even when `reviews[]` is given (kept as T2\'s legacy description text).' },
|
|
2003
2003
|
artifact_paths: { type: 'array', items: { type: 'string' }, description: 'Repo-relative paths the build binds (1-64). Exactly these files are stored as the bundle and hashed into build_ref; files the executor touches OUTSIDE them fail the scope check and park the cycle. No .., not absolute, no drive letter, no backslash.' },
|
|
2004
2004
|
task_type: { type: 'string', enum: ['CODE', 'SHELL'], description: 'T1 prefix (default CODE).' },
|
|
2005
2005
|
max_revisions: { type: 'integer', description: 'Revision cap (default 3): at most max_revisions + 1 builds and reviews.' },
|
|
@@ -2007,6 +2007,22 @@ const TOOLS = [
|
|
|
2007
2007
|
repo: { type: 'string', description: 'Which codebase on the executor the build runs in — an alias from the executor\'s allowlist (tascan-agent/repos.json), e.g. "tascan" (default), "merchskipper", "inkskipper", "rangerlizzy", "cardvault", "take1", "fitcheck", "safegrid", "eternity", "maniacs". artifact_paths are relative to that repo. An unknown alias is refused by the executor (repo_unknown) and the cycle parks.' },
|
|
2008
2008
|
max_cost_micro_usd: { type: 'integer', description: 'Per-cycle spend cap summed over every attempt, in micro-USD (default 5000000 = USD 5). A claim that could overrun it is refused (budget_exhausted).' },
|
|
2009
2009
|
preview_url: { type: 'string', description: 'Optional https preview link shown on the Decision task.' },
|
|
2010
|
+
reviews: {
|
|
2011
|
+
type: 'array',
|
|
2012
|
+
description: 'Optional multi-lens review panel (design item 14a) instead of today\'s single OpenAI review_brief lens — 1-8 entries, each: {lens: slug matching ^[a-z][a-z0-9-]{0,39}$ unique per array (e.g. "code-correctness", "security", "ui-ux"), brief: 1-8000 chars of cycle-specific context for that lens, provider: "openai"|"anthropic"|"gemini", model?: 1-100 chars, blocking?: boolean (default true — at least one entry must be blocking or the whole array is refused), max_tool_calls?: integer (openai only)}. One REVIEW: task mints per lens, all blocked on T1; the Decision task mints only once every blocking lens has approved the same build. Omit for today\'s single OpenAI code review.',
|
|
2013
|
+
items: {
|
|
2014
|
+
type: 'object',
|
|
2015
|
+
properties: {
|
|
2016
|
+
lens: { type: 'string', description: 'Slug, ^[a-z][a-z0-9-]{0,39}$, unique in the array.' },
|
|
2017
|
+
brief: { type: 'string', description: 'Cycle-specific context for this lens (1-8000 chars).' },
|
|
2018
|
+
provider: { type: 'string', enum: ['openai', 'anthropic', 'gemini'] },
|
|
2019
|
+
model: { type: 'string', description: 'Optional, 1-100 chars.' },
|
|
2020
|
+
blocking: { type: 'boolean', description: 'Default true. At least one entry in the array must be blocking.' },
|
|
2021
|
+
max_tool_calls: { type: 'integer', description: 'Optional, provider openai only.' }
|
|
2022
|
+
},
|
|
2023
|
+
required: ['lens', 'brief', 'provider']
|
|
2024
|
+
}
|
|
2025
|
+
},
|
|
2010
2026
|
checkpoint_title: { type: 'string', description: 'Optional title template for the Decision task (default "Decision: <title>").' },
|
|
2011
2027
|
checkpoint_description: { type: 'string', description: 'Optional description template for the Decision task; the build_ref, preview and findings summary are appended.' },
|
|
2012
2028
|
integrate_title: { type: 'string', description: 'Optional title template for the Integrate task (default "Integrate: <title>").' },
|
|
@@ -2022,6 +2038,7 @@ const TOOLS = [
|
|
|
2022
2038
|
const options = {};
|
|
2023
2039
|
if (args.repo) options.repo = String(args.repo);
|
|
2024
2040
|
for (const k of ['task_type', 'max_revisions', 'max_questions', 'max_cost_micro_usd', 'preview_url']) if (args[k] != null && args[k] !== '') options[k] = args[k];
|
|
2041
|
+
if (Array.isArray(args.reviews) && args.reviews.length) options.reviews = args.reviews;
|
|
2025
2042
|
const templates = {};
|
|
2026
2043
|
if (args.checkpoint_title || args.checkpoint_description) templates.checkpoint = { ...(args.checkpoint_title ? { title: args.checkpoint_title } : {}), ...(args.checkpoint_description ? { description: args.checkpoint_description } : {}) };
|
|
2027
2044
|
if (args.integrate_title || args.integrate_description) templates.integrate = { ...(args.integrate_title ? { title: args.integrate_title } : {}), ...(args.integrate_description ? { description: args.integrate_description } : {}) };
|
|
@@ -2033,7 +2050,10 @@ const TOOLS = [
|
|
|
2033
2050
|
if (result.duplicate || d.created === false) {
|
|
2034
2051
|
return `DUPLICATE — nothing was queued. A cycle with the same ${args.idempotency_key ? 'idempotency_key' : 'briefs and paths'} exists from the last 24 h (status ${d.status || 'active'}).\nroot_id: ${d.root_id}\nreview_id: ${d.review_id || 'n/a'}\n\nRead it: tascan_get_cycle_report root_id=${d.root_id}`;
|
|
2035
2052
|
}
|
|
2036
|
-
|
|
2053
|
+
const reviewLine = Array.isArray(d.review_ids) && d.review_ids.length > 0
|
|
2054
|
+
? `review_ids (T2 REVIEW panel, ${d.review_ids.length} lens${d.review_ids.length > 1 ? 'es' : ''}, blocked on T1): ${d.review_ids.join(', ')}`
|
|
2055
|
+
: `review_id (T2 REVIEW, blocked on T1): ${d.review_id}`;
|
|
2056
|
+
return `Cycle created.\nroot_id (T1 ${options.task_type || 'CODE'}: build): ${d.root_id}\n${reviewLine}\nbrief_sha256: ${d.brief_sha256}\ndecisions_list_id: ${d.decisions_list_id}\nquestions_list_id: ${d.questions_list_id}\nartifact_paths: ${paths.join(', ')}\n\nNext: the local executor claims T1 on its next poll (≤ 30 s), stores the bundle and finishes with build_ref; T2 is released from that finish. Follow with tascan_get_cycle_report root_id=${d.root_id} or tascan_get_task task_id=${d.root_id} (agent + trail blocks). Nobody is texted until the reviewer approves.`;
|
|
2037
2057
|
}
|
|
2038
2058
|
},
|
|
2039
2059
|
{
|
|
@@ -2142,7 +2162,10 @@ const TOOLS = [
|
|
|
2142
2162
|
''
|
|
2143
2163
|
];
|
|
2144
2164
|
lines.push('STEPS');
|
|
2145
|
-
|
|
2165
|
+
// design item 14a-1b: a review step whose coord carries a lens (multi-lens panel, migration 168) prints
|
|
2166
|
+
// 'review[<lens>]'; a legacy single-review step (no lens key) prints plain 'review', byte-identical to before.
|
|
2167
|
+
const kindLabel = st => st.kind === 'review' && st.lens ? `review[${st.lens}]` : st.kind;
|
|
2168
|
+
(s.steps || []).forEach(st => lines.push(` ${kindLabel(st)} r${st.revision}${st.seq ? '.' + st.seq : ''} ${st.title} [${st.blocked_by ? 'blocked' : st.completed ? 'completed' : st.released_at ? 'released' : 'open'}; ${st.attempts || 0} attempt(s)] ${st.id}`));
|
|
2146
2169
|
if (s.verdicts && s.verdicts.length) { lines.push('', 'VERDICTS'); s.verdicts.forEach(v => lines.push(` r${v.revision} ${v.verdict || 'missing'} on ${short(v.build_ref)} ${v.from_reviewer ? '' : '(NOT the reviewer identity) '}completion ${v.completion_id}`)); }
|
|
2147
2170
|
if (s.decisions && s.decisions.length) { lines.push('', 'DECISIONS'); s.decisions.forEach(v => lines.push(` ${v.decision} by ${v.actor_id} on ${short(v.build_ref)} completion ${v.completion_id || 'n/a'}`)); }
|
|
2148
2171
|
if (s.questions && s.questions.length) { lines.push('', 'QUESTIONS'); s.questions.forEach(q => lines.push(` to ${q.addressee}: ${q.title} — ${q.answered ? 'answered' : 'AWAITING ANSWER'} (${q.id})`)); }
|
|
@@ -2152,6 +2175,24 @@ const TOOLS = [
|
|
|
2152
2175
|
if (args.full) out += '\n\n' + JSON.stringify(d, null, 2);
|
|
2153
2176
|
return out.length > 12000 ? out.slice(0, 12000) + '\n…[truncated at 12000 chars — read sections with tascan_get_task / tascan_get_receipt]' : out;
|
|
2154
2177
|
}
|
|
2178
|
+
},
|
|
2179
|
+
{
|
|
2180
|
+
name: 'tascan_project_digest',
|
|
2181
|
+
description: 'One call that gives a chat client (ChatGPT, Claude) a whole TaScan project in about 2,000 words: the project, every task list with task counts, open decisions and questions, the last 5 coordination cycles with verdicts and spend, the latest 5 receipts, and total spend — returned as Markdown (capped at 12,000 chars). Read tier. Use it before asking a human to paste anything.',
|
|
2182
|
+
inputSchema: {
|
|
2183
|
+
type: 'object',
|
|
2184
|
+
properties: {
|
|
2185
|
+
project_id: { type: 'string', description: 'Project ID (UUID).' }
|
|
2186
|
+
},
|
|
2187
|
+
required: ['project_id']
|
|
2188
|
+
},
|
|
2189
|
+
annotations: { title: 'Project Digest', readOnlyHint: true, destructiveHint: false, idempotentHint: true, openWorldHint: false },
|
|
2190
|
+
handler: async (args, api) => {
|
|
2191
|
+
const id = String(args.project_id || '').trim().toLowerCase();
|
|
2192
|
+
if (!/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(id)) throw new Error('project_id must be a UUID');
|
|
2193
|
+
const result = await api('GET', `/projects/${id}/digest`);
|
|
2194
|
+
return result.markdown || '(no digest returned)';
|
|
2195
|
+
}
|
|
2155
2196
|
}
|
|
2156
2197
|
];
|
|
2157
2198
|
|