@orboto/mcp 0.208.0 → 0.209.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/dist/server.js +2 -0
- package/dist/tools/admin-translations.js +1 -1
- package/dist/tools/agent-coordination.js +1 -1
- package/dist/tools/bulk-writes.js +4 -4
- package/dist/tools/doc-attachments.js +1 -1
- package/dist/tools/doc-edits.js +1 -1
- package/dist/tools/doc-export.js +1 -1
- package/dist/tools/docs.js +2 -2
- package/dist/tools/identity.js +1 -1
- package/dist/tools/list-git-app-installations.js +1 -1
- package/dist/tools/primer-facts.js +1 -1
- package/dist/tools/raci.js +1 -1
- package/dist/tools/review-policy.js +1 -1
- package/dist/tools/secret-scan.js +54 -0
- package/dist/tools/ticket-attachments.js +2 -2
- package/dist/tools/ticket-writes.js +2 -2
- package/dist/tools/time-writes.js +1 -1
- package/dist/tools/wiki.js +1 -1
- package/dist/tools/work-next.js +1 -1
- package/dist/tools/work-sessions.js +2 -2
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -14,6 +14,7 @@ import { aiStatusToolConfig, makeAiStatusHandler } from './tools/ai-status.js';
|
|
|
14
14
|
import { draftCustomerReplyToolConfig, makeDraftCustomerReplyHandler } from './tools/customer-draft.js';
|
|
15
15
|
import { embeddingStatusToolConfig, makeEmbeddingStatusHandler } from './tools/embedding-status.js';
|
|
16
16
|
import { aiUsageToolConfig, makeAiUsageHandler } from './tools/ai-usage.js';
|
|
17
|
+
import { secretScanToolConfig, makeSecretScanHandler } from './tools/secret-scan.js';
|
|
17
18
|
import { sessionStartToolConfig, makeSessionStartHandler } from './tools/session-start.js';
|
|
18
19
|
import { CHANNEL_CAPABILITY, CHANNEL_INSTRUCTIONS } from './inbox-channel.js';
|
|
19
20
|
import { loadRequiredRules } from './required-rules.js';
|
|
@@ -195,6 +196,7 @@ export async function buildOrbotoMcpServer(opts) {
|
|
|
195
196
|
reg('orboto_draft_customer_reply', draftCustomerReplyToolConfig, makeDraftCustomerReplyHandler(client));
|
|
196
197
|
reg('orboto_embedding_status', embeddingStatusToolConfig, makeEmbeddingStatusHandler(client));
|
|
197
198
|
reg('orboto_ai_usage', aiUsageToolConfig, makeAiUsageHandler(client));
|
|
199
|
+
reg('orboto_secret_scan', secretScanToolConfig, makeSecretScanHandler(client));
|
|
198
200
|
reg('orboto_session_start', sessionStartToolConfig, makeSessionStartHandler(client, { channel: opts.channel }));
|
|
199
201
|
reg('orboto_response_expand', responseExpandToolConfig, makeResponseExpandHandler());
|
|
200
202
|
reg('orboto_help', helpToolConfig, makeHelpHandler());
|
|
@@ -55,7 +55,7 @@ export function makeApproveTranslationHandler(client) {
|
|
|
55
55
|
}
|
|
56
56
|
export const revertTranslationToolConfig = {
|
|
57
57
|
title: 'Revert an auto-translated ticket back to the original',
|
|
58
|
-
description: 'Restore the pre-translation title + description from the
|
|
58
|
+
description: 'Restore the pre-translation title + description from the audit comment and clear the auto-translate marker + prior approval. Use when the AI got the translation wrong and the operator wants to start over. 409 if the ticket was never auto-translated. Requires `admin:translation_review`.',
|
|
59
59
|
inputSchema: z.object({
|
|
60
60
|
ticketId: z.string().uuid().describe('UUID of the ticket to revert.'),
|
|
61
61
|
}).shape,
|
|
@@ -42,7 +42,7 @@ export function makeAgentHeartbeatHandler(client) {
|
|
|
42
42
|
}
|
|
43
43
|
export const agentPresenceToolConfig = {
|
|
44
44
|
title: 'Workspace agent presence',
|
|
45
|
-
description: 'Returns all active orboto agent instances
|
|
45
|
+
description: 'Returns all active orboto agent instances: lane workers/reviewers, inbox and MCP connections, registered runners. Session freshness is 90 seconds; lanes retain their 300-second heartbeat grace. Unexpired work leases keep their owning instance visible. Ticket metadata and claims retain ordinary ticket visibility. Rows include `kind` (agent or human, classified by the credential: agent API key, OAuth grant, lane, external token or bot account; `isBot` is its alias), `actsAs` (the human account an AI client acts under), `projects` (memberships plus lane projects), `connections` (type + label per credential or attachment, `live_events` for the MCP event bridge), owner, lane and stable sessionId; separate instances of one account remain separate. Callers with admin:system:read see every agent; other users see only their own sessions. Each row exposes `userId`, `userEmail`, the agent runtime (`clientInfo.name`), declared `capabilities`, current `status`, the ticket it works on, and `reconnects` survived under the same id and scope. Check it before dispatching a sub-task so you do not step on a parallel run. Filters `projectKey` (rows whose `projects` contain the key) and `kind` (agent | human) narrow the list client-side, the same way `orboto agents --project KEY --bots|--humans` does.',
|
|
46
46
|
inputSchema: z.object({
|
|
47
47
|
projectKey: z.string().min(1).max(64).optional().describe('Keep only rows whose projects contain this project key (case-insensitive).'),
|
|
48
48
|
kind: AgentInventoryKindSchema.optional().describe('Keep only rows classified as agent or human by their credential.'),
|
|
@@ -140,7 +140,7 @@ export function makeBulkPatchTicketsHandler(client) {
|
|
|
140
140
|
}
|
|
141
141
|
export const bulkMoveTicketsToolConfig = {
|
|
142
142
|
title: 'Move many tickets to a status category',
|
|
143
|
-
description: 'Move every ticket in `ticketKeys` (or
|
|
143
|
+
description: 'Move every ticket in `ticketKeys` (or `query` selects) to the same status category (todo/in_progress/in_review/done/wont_fix). Each ticket lands on its own project\'s first status with that category. Returns the per-ticket outcome.',
|
|
144
144
|
inputSchema: z.object({
|
|
145
145
|
ticketKeys: TICKET_KEY_ARRAY,
|
|
146
146
|
query: QUERY_SOURCE,
|
|
@@ -173,7 +173,7 @@ export function makeBulkMoveTicketsHandler(client) {
|
|
|
173
173
|
}
|
|
174
174
|
export const bulkCloseTicketsToolConfig = {
|
|
175
175
|
title: 'Close many tickets (optionally with a shared comment)',
|
|
176
|
-
description: 'For every ticket in `ticketKeys` (or
|
|
176
|
+
description: 'For every ticket in `ticketKeys` (or `query` selects): optionally post the same `comment` first, then move to `done`. Mirrors `orboto.mjs bulk-close`. Comment-first ordering means the close note lands in the audit trail even if the status PATCH 403s.',
|
|
177
177
|
inputSchema: z.object({
|
|
178
178
|
ticketKeys: TICKET_KEY_ARRAY,
|
|
179
179
|
query: QUERY_SOURCE,
|
|
@@ -261,7 +261,7 @@ async function makeAssigneeResolver(client, email) {
|
|
|
261
261
|
}
|
|
262
262
|
export const bulkAssignTicketsToolConfig = {
|
|
263
263
|
title: 'Assign the same user to many tickets',
|
|
264
|
-
description: 'POST `assigneeEmail` as an additional assignee on every ticket in `ticketKeys` (or
|
|
264
|
+
description: 'POST `assigneeEmail` as an additional assignee on every ticket in `ticketKeys` (or `query` selects). Multi-assignee: this adds, never replaces. Idempotent: a 409 (already assigned) counts as success.',
|
|
265
265
|
inputSchema: z.object({
|
|
266
266
|
ticketKeys: TICKET_KEY_ARRAY,
|
|
267
267
|
query: QUERY_SOURCE,
|
|
@@ -304,7 +304,7 @@ export function makeBulkAssignTicketsHandler(client) {
|
|
|
304
304
|
}
|
|
305
305
|
export const bulkUnassignTicketsToolConfig = {
|
|
306
306
|
title: 'Unassign the same user from many tickets',
|
|
307
|
-
description: 'DELETE `assigneeEmail` from every ticket in `ticketKeys` (or
|
|
307
|
+
description: 'DELETE `assigneeEmail` from every ticket in `ticketKeys` (or `query` selects). Idempotent: a 404 (was not assigned) counts as success.',
|
|
308
308
|
inputSchema: z.object({
|
|
309
309
|
ticketKeys: TICKET_KEY_ARRAY,
|
|
310
310
|
query: QUERY_SOURCE,
|
|
@@ -37,7 +37,7 @@ function isImage(mimetype) {
|
|
|
37
37
|
}
|
|
38
38
|
export const uploadDocAttachmentToolConfig = {
|
|
39
39
|
title: 'Upload an attachment to a doc page',
|
|
40
|
-
description: 'Upload a file as
|
|
40
|
+
description: 'Upload a file as a doc attachment: returns its Markdown image (or link) line + a stable download URL. Bytes come in as base64 (`contentBase64`) so this works without local FS access on the agent side. Set `embed=true` to also PATCH the doc body to append the Markdown line - useful for dropping a screenshot into a page in one call. MIME / extension policy is enforced server-side: blocked types (HTML, SVG, executables) surface a 415.',
|
|
41
41
|
inputSchema: z.object({
|
|
42
42
|
docId: z.string().min(1).describe('Doc UUID or human-readable doc key (ORB-D12 / DOC-5).'),
|
|
43
43
|
filename: z.string().min(1).describe('Display filename, e.g. "architecture-diagram.png".'),
|
package/dist/tools/doc-edits.js
CHANGED
|
@@ -167,7 +167,7 @@ export function makeEditDocHandler(client) {
|
|
|
167
167
|
}
|
|
168
168
|
export const editDocSectionToolConfig = {
|
|
169
169
|
title: 'Edit a doc section by heading path (context-efficient)',
|
|
170
|
-
description: 'Change a doc by
|
|
170
|
+
description: 'Change a doc by heading-addressed section ops - ship only the new section content, not the whole doc. A section is addressed by its heading path (e.g. ["Setup","Credentials"] = a "Credentials" heading nested under "Setup"). Heading matching is EXACT + case-sensitive; the leading path elements must be an ordered ancestor chain (not necessarily immediate parents). op=replace swaps the section body, op=append adds to its end, op=insertAfter inserts directly below the heading line (the heading line itself is never removed). A path that matches zero or multiple headings returns a 409 you can act on (narrow the path to disambiguate). Prefer this over orboto_update_doc for section-scoped rewrites. baseRevisionId gives optimistic concurrency. Returns a short context window per op.',
|
|
171
171
|
inputSchema: z.object({
|
|
172
172
|
docId: z.string().min(1).describe('Doc UUID or human-readable key (ORB-D12 / DOC-5). Find via orboto_search_docs.'),
|
|
173
173
|
sectionOps: z.array(z.object({
|
package/dist/tools/doc-export.js
CHANGED
|
@@ -27,7 +27,7 @@ export function makeExportDocMdHandler(client) {
|
|
|
27
27
|
}
|
|
28
28
|
export const exportDocPdfToolConfig = {
|
|
29
29
|
title: 'Export a doc page as PDF',
|
|
30
|
-
description: 'Render the doc page to PDF
|
|
30
|
+
description: 'Render the doc page to PDF (Puppeteer-backed PdfService) and return the bytes as a base64 MCP resource attachment. Requires the PDF engine to be configured - deployments without Chromium / Puppeteer return 503 (errors.pdf.engine_unavailable) which surfaces as an OrbotoApiError.',
|
|
31
31
|
inputSchema: z.object({
|
|
32
32
|
docId: z.string().min(1).describe('Doc UUID or human-readable doc key (ORB-D12 / DOC-5).'),
|
|
33
33
|
}).shape,
|
package/dist/tools/docs.js
CHANGED
|
@@ -216,7 +216,7 @@ export function makeDeleteDocSpaceHandler(client) {
|
|
|
216
216
|
}
|
|
217
217
|
export const listDocsInSpaceToolConfig = {
|
|
218
218
|
title: 'List docs inside a space',
|
|
219
|
-
description: 'Return the flat list of doc pages in a space - each carries `parentDocId`
|
|
219
|
+
description: 'Return the flat list of doc pages in a space - each carries `parentDocId` to reconstruct the tree client-side. Use this when an agent needs to find a doc by title rather than asking for a UUID first. Pair with orboto_get_doc to read individual page bodies.',
|
|
220
220
|
inputSchema: z.object({
|
|
221
221
|
spaceId: z.string().min(1).describe('Space key (e.g. ORB-S1), name, or UUID. Discover via orboto_list_doc_spaces.'),
|
|
222
222
|
}).shape,
|
|
@@ -436,7 +436,7 @@ export function makeDuplicateDocSpaceHandler(client) {
|
|
|
436
436
|
}
|
|
437
437
|
export const resolveDocSmartLinksToolConfig = {
|
|
438
438
|
title: 'Batch-resolve smart-link references to display metadata',
|
|
439
|
-
description: 'Resolve `[[doc:UUID]]
|
|
439
|
+
description: 'Resolve `[[doc:UUID]]`/`[[ticket:UUID]]`/`[[milestone:UUID]]`/`[[project:UUID]]`/commit-hash references to their title + URL. Visibility-filtered - items the caller is not allowed to see come back as missing (the rendering frontend falls back to the literal label in that case). Useful when reading a doc body that contains many tokens and you want to display them with current titles in one round-trip rather than N+1 get-* calls. Max 200 items per call.',
|
|
440
440
|
inputSchema: z.object({
|
|
441
441
|
items: z.array(z.object({
|
|
442
442
|
type: z.enum(['doc', 'ticket', 'milestone', 'project', 'commit']),
|
package/dist/tools/identity.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import { z } from 'zod';
|
|
5
5
|
export const whoamiToolConfig = {
|
|
6
6
|
title: 'Show the authenticated MCP principal',
|
|
7
|
-
description: 'Return the authenticated user record `{id, email, fullName, isBot, workspaceLocale}`
|
|
7
|
+
description: 'Return the authenticated user record `{id, email, fullName, isBot, workspaceLocale}` for the API key this server runs with. Useful for debugging which credential / bot identity is in use when multiple MCP configs are wired to different `orb_*` keys. **`workspaceLocale` (ORB-989)** is the language the workspace expects tickets / comments / docs in - check it before a mass-create so you write in the right language from the start instead of relying on the after-the-fact language-mismatch warning.',
|
|
8
8
|
inputSchema: z.object({}).shape,
|
|
9
9
|
outputSchema: z.object({
|
|
10
10
|
id: z.string(),
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { z } from 'zod';
|
|
12
12
|
export const listGitAppInstallationsToolConfig = {
|
|
13
13
|
title: 'List GitHub App installations',
|
|
14
|
-
description: 'Every GitHub App installation orboto knows
|
|
14
|
+
description: 'Every GitHub App installation orboto knows, across projects (super-admin): org/user, install time, suspended state.',
|
|
15
15
|
inputSchema: z.object({}).shape,
|
|
16
16
|
annotations: { readOnlyHint: true, idempotentHint: true },
|
|
17
17
|
};
|
|
@@ -53,7 +53,7 @@ function summariseFact(f) {
|
|
|
53
53
|
}
|
|
54
54
|
export const primerFactListToolConfig = {
|
|
55
55
|
title: 'List structured project primer facts',
|
|
56
|
-
description: 'List structured project facts
|
|
56
|
+
description: 'List structured project facts feeding the AI primer (stack, conventions, deployment, architecture, integrations, gotchas, commands). Use this to discover what the project has already documented before adding a new observation. Workspace-wide facts (applying to every project) are merged in by default; pass includeWorkspace=false to see only project-scoped rows.',
|
|
57
57
|
inputSchema: z.object({
|
|
58
58
|
projectKey: z.string().min(1).describe('Project key, e.g. "ORB". Case-insensitive.'),
|
|
59
59
|
category: PrimerFactCategoryEnum.optional().describe('Filter by category. Omit for all categories.'),
|
package/dist/tools/raci.js
CHANGED
|
@@ -60,7 +60,7 @@ export function makeRaciHandler(client) {
|
|
|
60
60
|
}
|
|
61
61
|
export const setRaciToolConfig = {
|
|
62
62
|
title: 'Set a RACI role',
|
|
63
|
-
description: "Set a person's RACI role
|
|
63
|
+
description: "Set a person's RACI role: R (Responsible), A (Accountable, max one per ticket), C (Consulted), I (Informed). RACI is OPT-IN per project: only use this when the project has RACI enabled (`raciEnabled` true) - do NOT suggest or set RACI on projects that haven't opted in. Resolves the user by email within the ticket's project. Requires ticket:manage_raci. A second Accountable is rejected with the current holder's name.",
|
|
64
64
|
inputSchema: z.object({
|
|
65
65
|
ticketKey: z.string().min(3).describe('Ticket key (e.g. "ORB-42").'),
|
|
66
66
|
userEmail: z.string().email().describe('Email of a project member.'),
|
|
@@ -52,7 +52,7 @@ export function makeReviewPolicyCheckHandler(client) {
|
|
|
52
52
|
}
|
|
53
53
|
export const reviewApprovalRecordToolConfig = {
|
|
54
54
|
title: 'Record a review decision against a diff fingerprint',
|
|
55
|
-
description: 'Record your review verdict (approve/reject) against the
|
|
55
|
+
description: 'Record your review verdict (approve/reject) against the version-prefixed fingerprint from orboto_review_fingerprint. A current-algorithm approval is reusable only for identical submitted diff text; even whitespace or file-order changes require a fresh review. Legacy/unversioned records remain history only. This advisory review ledger does not itself enforce ticket closure. Requires ticket:record_review_approval.',
|
|
56
56
|
inputSchema: z.object({
|
|
57
57
|
ticketKey: z.string().min(3).describe('Ticket key, e.g. ORB-42.'),
|
|
58
58
|
fingerprint: z.string().min(8).describe('From orboto_review_fingerprint.'),
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ORB-2229 - `orboto_secret_scan`, the admin secret scan.
|
|
3
|
+
*/
|
|
4
|
+
import { z } from 'zod';
|
|
5
|
+
export const secretScanToolConfig = {
|
|
6
|
+
title: 'Secret scan',
|
|
7
|
+
description: 'Admin sweep for stored secret-shaped values (ORB-2229); see skill reference admin.md "Secret scan".',
|
|
8
|
+
inputSchema: z.object({
|
|
9
|
+
redact: z.boolean().optional().describe('Replace found values, purge history (default false).'),
|
|
10
|
+
limit: z.number().int().min(1).max(1000).optional().describe('Max hits (default 200).'),
|
|
11
|
+
}).shape,
|
|
12
|
+
annotations: { readOnlyHint: false, destructiveHint: true, idempotentHint: true },
|
|
13
|
+
};
|
|
14
|
+
export function makeSecretScanHandler(client) {
|
|
15
|
+
return async (args) => {
|
|
16
|
+
const report = await client.post('/admin/content/secret-scan', {
|
|
17
|
+
redact: args.redact ?? false,
|
|
18
|
+
limit: args.limit ?? 200,
|
|
19
|
+
});
|
|
20
|
+
const lines = [];
|
|
21
|
+
const counts = Object.entries(report.scanned).map(([k, n]) => `${k} ${n}`).join(', ');
|
|
22
|
+
lines.push(`Secret scan at ${report.scannedAt} - scanned ${counts}`);
|
|
23
|
+
if (report.hits.length === 0) {
|
|
24
|
+
lines.push('No secret-shaped value found.');
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
for (const hit of report.hits) {
|
|
28
|
+
const where = [hit.label, hit.projectKey ? `(${hit.projectKey})` : null, hit.spaceVisibility ? `[space: ${hit.spaceVisibility}]` : null].filter(Boolean).join(' ');
|
|
29
|
+
const state = hit.redacted ? ' - redacted' : '';
|
|
30
|
+
lines.push(`${hit.entity} ${where}${state}`);
|
|
31
|
+
for (const f of hit.findings) {
|
|
32
|
+
lines.push(` ${f.class} in ${hit.field} at line ${f.line}, column ${f.column} (${f.preview}, ${f.length} chars)`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
lines.push(report.redacted > 0
|
|
36
|
+
? `Redacted ${report.redacted} rows; rotate every credential listed above.`
|
|
37
|
+
: `${report.hits.length} hits; rotate every credential listed above, then run with redact: true.`);
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
content: [{ type: 'text', text: lines.join('\n') }],
|
|
41
|
+
structuredContent: {
|
|
42
|
+
scannedAt: report.scannedAt,
|
|
43
|
+
scanned: report.scanned,
|
|
44
|
+
hitCount: report.hits.length,
|
|
45
|
+
redactedCount: report.redacted,
|
|
46
|
+
hits: report.hits.map((h) => ({
|
|
47
|
+
entity: h.entity, id: h.id, label: h.label, field: h.field,
|
|
48
|
+
projectKey: h.projectKey, spaceVisibility: h.spaceVisibility, webUrl: h.webUrl,
|
|
49
|
+
classes: h.findings.map((f) => f.class), redacted: h.redacted,
|
|
50
|
+
})),
|
|
51
|
+
},
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
}
|
|
@@ -14,7 +14,7 @@ function isImage(mimetype) {
|
|
|
14
14
|
}
|
|
15
15
|
export const listTicketAttachmentsToolConfig = {
|
|
16
16
|
title: 'List attachments on a ticket',
|
|
17
|
-
description: 'Return a ticket\'s attachments newest-first
|
|
17
|
+
description: 'Return a ticket\'s attachments newest-first: id, filename, content type, size, stable download URL. Use the returned id with orboto_get_attachment to actually view an image or fetch the bytes. Empty list = no attachments. Input is the ticket key like "ACME-42".',
|
|
18
18
|
inputSchema: z.object({
|
|
19
19
|
ticketKey: z.string().min(3).describe('Ticket key like "ACME-42".'),
|
|
20
20
|
}).shape,
|
|
@@ -57,7 +57,7 @@ export function makeListTicketAttachmentsHandler(client) {
|
|
|
57
57
|
}
|
|
58
58
|
export const getAttachmentToolConfig = {
|
|
59
59
|
title: 'View or fetch an attachment\'s bytes',
|
|
60
|
-
description: 'Fetch
|
|
60
|
+
description: 'Fetch an attachment by id (from orboto_list_ticket_attachments or orboto_get_ticket) and return its content. For an image, the model receives an image content block so it actually SEES the screenshot, plus a text line with filename and size. For a non-image, the model gets a text block with metadata and, for small files, the base64 content; large binaries point at the skill\'s download-attachment shortcut. Works for ticket, doc, and comment attachments (the id is global). Enforces the same project/space access check as listing the attachment.',
|
|
61
61
|
inputSchema: z.object({
|
|
62
62
|
attachmentId: z.string().uuid().describe('Attachment UUID from a list/get call.'),
|
|
63
63
|
}).shape,
|
|
@@ -403,7 +403,7 @@ export function makeCloseTicketHandler(client) {
|
|
|
403
403
|
}
|
|
404
404
|
export const deleteTicketToolConfig = {
|
|
405
405
|
title: 'Permanently delete a ticket',
|
|
406
|
-
description: 'DESTRUCTIVE, IRREVERSIBLE hard-delete of a ticket (by key):
|
|
406
|
+
description: 'DESTRUCTIVE, IRREVERSIBLE hard-delete of a ticket (by key): row and history gone, a `ticket.deleted` event + webhook fire. Strongly prefer moving the ticket to `wont_fix` (orboto_move_ticket) or closing it instead - wont_fix keeps the history and analytics intact. Only hard-delete a ticket that should truly never have existed (accidental duplicate, spam). Caller must have `ticket:delete`.',
|
|
407
407
|
inputSchema: z.object({
|
|
408
408
|
ticketKey: z.string().min(3),
|
|
409
409
|
}).shape,
|
|
@@ -719,7 +719,7 @@ export function makeRemoveTicketDependencyHandler(client) {
|
|
|
719
719
|
}
|
|
720
720
|
export const listTicketDependenciesToolConfig = {
|
|
721
721
|
title: 'List a ticket\'s dependencies',
|
|
722
|
-
description: 'Show both directions of
|
|
722
|
+
description: 'Show both directions of a ticket\'s dependency graph: `blockedBy` (must finish first) and `blocks` (waiting on this one). ORB-1614: an edge to a ticket in another project you cannot read comes back as an opaque "external dependency" entry (no title/key/status - just whether it is still open).',
|
|
723
723
|
inputSchema: z.object({
|
|
724
724
|
ticketKey: z.string().min(3),
|
|
725
725
|
}).shape,
|
|
@@ -165,7 +165,7 @@ export function makeListTimeEntriesHandler(client) {
|
|
|
165
165
|
}
|
|
166
166
|
export const editTimeEntryToolConfig = {
|
|
167
167
|
title: 'Edit / correct a time entry',
|
|
168
|
-
description: 'Correct a time entry
|
|
168
|
+
description: 'Correct a time entry: fix a duration, edit its note, change its date, or re-target it to another ticket. Use THIS (not orboto_log_time) to fix a wrong entry: log_time is additive and only makes the total worse. Get the entryId from orboto_list_time_entries (or an orboto_log_time response). Blocked if the entry is locked by an approved timesheet.',
|
|
169
169
|
inputSchema: z.object({
|
|
170
170
|
ticketKey: z.string().min(3).describe('The ticket the entry currently belongs to.'),
|
|
171
171
|
entryId: z.string().uuid(),
|
package/dist/tools/wiki.js
CHANGED
|
@@ -71,7 +71,7 @@ export function makeWikiLintHandler(client) {
|
|
|
71
71
|
}
|
|
72
72
|
export const wikiPlanUpdateToolConfig = {
|
|
73
73
|
title: 'Plan a wiki edit (dry-run, no writes)',
|
|
74
|
-
description: 'Turn a natural-language instruction into
|
|
74
|
+
description: 'Turn a natural-language instruction into page operations (create/patch/append) WITHOUT writing anything. Returns a planId valid for 15 minutes plus the proposed ops. Review the ops, then call orboto_wiki_apply_plan to commit. Wraps POST /spaces/:id/docs/plan-update.',
|
|
75
75
|
inputSchema: z.object({
|
|
76
76
|
spaceId: z.string().uuid().describe('The wiki space to edit.'),
|
|
77
77
|
instruction: z.string().min(1).max(4000).describe('What to change, in plain language.'),
|
package/dist/tools/work-next.js
CHANGED
|
@@ -23,7 +23,7 @@ function landedIdleLines(rows) {
|
|
|
23
23
|
}
|
|
24
24
|
export const workNextToolConfig = {
|
|
25
25
|
title: 'Pull and reserve the next ready ticket (worker-pool dispatch)',
|
|
26
|
-
description: 'ORB-1613 - the pull side of low-management dispatch, sibling of orboto_work_start
|
|
26
|
+
description: 'ORB-1613 - the pull side of low-management dispatch, sibling of orboto_work_start for "any ticket will do". Picks the highest-priority ticket in a project that is READY FOR THE REQUESTED ROLE (default role implementation pulls TODO tickets; `role: "review"` pulls tickets in the in_review status category instead, and NEVER offers a ticket the caller itself implemented - the review lane primitive, ORB-1777), unblocked (every dependency closed), not already leased under the requested role, and not blocked by a conflicting resourceClaim - then reserves it with the EXACT same guarantees as orboto_work_start (atomic lease acquire + the full context bundle: rules ack, primer, ticket, checklists, dependencies, git health, siblings) in the same response. Priority then ticket number, deterministic - never a coin flip on ties. Two workers calling this concurrently never receive the SAME ticket: the underlying reservation is the identical partial-unique-index INSERT orboto_work_start uses, just walked across an ordered candidate list - a collision just advances to the next candidate. Epics are never returned (they are containers, not directly implementable). When nothing is ready, the response is a STRUCTURED result (`reserved: null`) with a `reason` (`none-matching` = no todo tickets at all; `all-blocked` = candidates exist but all have open dependencies; `all-leased` = ready candidates exist but are all currently leased or claim-conflicted; `autonomy_paused` = ORB-1774, this agent identity or the whole workspace has autonomous pulls paused by an operator - idle and wait for an operator/notify wakeup instead of retrying) and, ONLY when derivable from an actual active lease, a `retryAfterSeconds` backoff hint (`earliestFreeAt` null and `retryAfterSeconds` null means no signal exists - never a fabricated constant). This is never an error - a worker pool should back off on the hint rather than poll hot. Prefer this over orboto_work_start whenever the caller does not care WHICH ticket it gets, only that it gets the best available one right now. Every response - reserved, empty or paused - also carries `landedIdle`: YOUR OWN tickets in this project that are still in_progress with a linked commit and no activity for days (ORB-1799). Those are finished-looking work nobody handed to the review lane; move them on instead of re-implementing them.',
|
|
27
27
|
inputSchema: z.object({
|
|
28
28
|
projectKey: z.string().min(1).describe('Project key (e.g. "ACME") or UUID.'),
|
|
29
29
|
agentTag: z.string().min(1).max(64).optional()
|
|
@@ -456,7 +456,7 @@ export function makeWorkSessionsHandler(client) {
|
|
|
456
456
|
}
|
|
457
457
|
export const workSessionClaimsAddToolConfig = {
|
|
458
458
|
title: 'Add resource claims to a live work session',
|
|
459
|
-
description: 'ORB-1610 - declare more resource claims on a session you
|
|
459
|
+
description: 'ORB-1610 - declare more resource claims on a session you hold, without touching the lease or timer. Use this when you did not know the files/resources you would touch at start time, or need to widen scope mid-task. Same conflict rule as start: `write` claims conflict with any OVERLAPPING active write claim workspace-wide; `read` claims never conflict. Default `onConflict: "reject"` fails the WHOLE call (nothing is added) and names every conflicting holder; `queue` accepts the conflicting ones as `state: "waiting"`.',
|
|
460
460
|
inputSchema: z.object({
|
|
461
461
|
sessionId: z.string().uuid().describe('The id returned by orboto_work_session_start.'),
|
|
462
462
|
claims: z.array(z.object(ResourceClaimShape)).min(1).max(50)
|
|
@@ -501,7 +501,7 @@ export function makeWorkSessionClaimsAddHandler(client) {
|
|
|
501
501
|
}
|
|
502
502
|
export const workSessionClaimsReleaseToolConfig = {
|
|
503
503
|
title: 'Release resource claims from a live work session',
|
|
504
|
-
description: 'ORB-1610 - drop
|
|
504
|
+
description: 'ORB-1610 - drop resource claims (or all) from a session you hold, WITHOUT finishing it or touching the ticket lease/timer. Releasing a granted write claim immediately runs a grant pass, so the earliest queued waiter for that resource is promoted as part of this call - useful once you know you are done touching a subtree but are not done with the ticket. Omit `claims` to release everything the session holds.',
|
|
505
505
|
inputSchema: z.object({
|
|
506
506
|
sessionId: z.string().uuid().describe('The id returned by orboto_work_session_start.'),
|
|
507
507
|
claims: z.array(z.object({ kind: z.enum(['path', 'named']), value: z.string().min(1).max(500) })).optional()
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@orboto/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.209.1",
|
|
4
4
|
"description": "Model Context Protocol server for orboto - exposes tickets, projects, milestones, time, docs, primer-facts, alerts and absences as MCP tools for Claude Desktop / Cursor / Copilot Chat / any MCP-aware AI client.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|