@manudota/artist-mcp 0.1.0 → 0.2.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/agent-pack/.artist/policies/LOCAL_STATE.md +36 -0
- package/agent-pack/.artist/project-types/CONCERT.md +24 -0
- package/agent-pack/.artist/project-types/LARGE_CONCERT.md +19 -0
- package/agent-pack/.artist/project-types/REHEARSAL.md +21 -0
- package/agent-pack/.artist/project-types/STUDIO_SESSION.md +22 -0
- package/agent-pack/.artist/roles/ARCHIVIST.md +19 -0
- package/agent-pack/.artist/roles/AUDITOR.md +20 -0
- package/agent-pack/.artist/roles/ENVOY.md +19 -0
- package/agent-pack/.artist/roles/JANITOR.md +18 -0
- package/agent-pack/.artist/roles/ORCHESTRATOR.md +21 -0
- package/agent-pack/.artist/roles/PROJECT_MANAGER.md +20 -0
- package/agent-pack/.artist/roles/REGISTRAR.md +19 -0
- package/agent-pack/AGENTS.md +23 -0
- package/agent-pack/registry.json +101 -0
- package/dist/agents.js +156 -0
- package/dist/client.js +4 -4
- package/dist/config.js +6 -6
- package/dist/index.js +16 -7
- package/dist/init.js +4 -4
- package/dist/server.js +36 -7
- package/package.json +9 -3
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# Local State
|
|
2
|
+
|
|
3
|
+
Use local state only when it helps the musician resume work without rereading
|
|
4
|
+
the same context. The backend remains stateless: it stores connection
|
|
5
|
+
credentials, never agent memory or copies of OneNote content.
|
|
6
|
+
|
|
7
|
+
## Storage boundary
|
|
8
|
+
|
|
9
|
+
- Store optional agent-owned working data only under `.artist/local/`.
|
|
10
|
+
- Treat local state as disposable and user-controlled. The workflow must still
|
|
11
|
+
work when the directory is missing or deleted.
|
|
12
|
+
- Choose the smallest legible format for the job. Prefer Markdown for working
|
|
13
|
+
notes and JSON for structured facts. Use SQLite only when the amount or shape
|
|
14
|
+
of local data genuinely requires it.
|
|
15
|
+
- Keep short derived preferences, decisions, and resumable working summaries.
|
|
16
|
+
Do not mirror entire OneNote pages, contact books, calendars, or message
|
|
17
|
+
histories.
|
|
18
|
+
- Never store access tokens, refresh tokens, connection keys, environment
|
|
19
|
+
variables, or other secrets in `.artist/local/`.
|
|
20
|
+
|
|
21
|
+
## No coordination infrastructure
|
|
22
|
+
|
|
23
|
+
Local state is not an agent coordination protocol. Do not create claims,
|
|
24
|
+
locks, queues, leases, assignments, review records, concurrency control, or
|
|
25
|
+
maintenance daemons. Roles are plain-language lenses loaded for one item at a
|
|
26
|
+
time.
|
|
27
|
+
|
|
28
|
+
The Auditor may assess an Envoy draft in the current chat, and the Janitor may
|
|
29
|
+
propose stale items for the musician to clear. Neither action creates a review
|
|
30
|
+
system or authorizes an external write.
|
|
31
|
+
|
|
32
|
+
## Read-only MVP
|
|
33
|
+
|
|
34
|
+
All source facts are fetched from the current OneNote working-unit page. Show
|
|
35
|
+
the result in chat. The musician remains responsible for editing, sending,
|
|
36
|
+
booking, closing, deleting, and changing connected applications.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Concert
|
|
2
|
+
|
|
3
|
+
## Working unit
|
|
4
|
+
|
|
5
|
+
One OneNote page describing one concert. Expected fields include name, date,
|
|
6
|
+
venue, audience, project lead, budget, description, milestones, tasks, and
|
|
7
|
+
musician or supplier contacts.
|
|
8
|
+
|
|
9
|
+
## Workflow
|
|
10
|
+
|
|
11
|
+
1. Verify the headline facts and find contradictory dates.
|
|
12
|
+
2. Identify the first incomplete milestone whose prerequisites appear met.
|
|
13
|
+
3. Find the corresponding task, owner, deadline, and relevant contact.
|
|
14
|
+
4. Recommend one next action.
|
|
15
|
+
5. If outreach is useful, Registrar extracts the contact, Envoy drafts, and
|
|
16
|
+
Auditor verifies before the result is shown.
|
|
17
|
+
|
|
18
|
+
## Checks
|
|
19
|
+
|
|
20
|
+
- Venue, artist, marketing, ticketing, technical, event-day, and settlement.
|
|
21
|
+
- Milestone dates agree with task dates and the headline event date.
|
|
22
|
+
- Completed tasks are not proposed again.
|
|
23
|
+
- Historical comparisons name their source page.
|
|
24
|
+
- Audience and budget differences are context, not proof of complexity.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Large Concert or Festival
|
|
2
|
+
|
|
3
|
+
## Working unit
|
|
4
|
+
|
|
5
|
+
One OneNote page for a concert whose evidence shows additional production
|
|
6
|
+
complexity, such as a large audience, multiple artists, outdoor staging,
|
|
7
|
+
security, ticketing, volunteers, or substantial technical dependencies.
|
|
8
|
+
|
|
9
|
+
## Workflow
|
|
10
|
+
|
|
11
|
+
Use the Concert workflow, then check production, staffing, security, ticketing,
|
|
12
|
+
artist contracting, weather or access dependencies, and post-event settlement.
|
|
13
|
+
Choose one action on the critical path rather than listing every open task.
|
|
14
|
+
|
|
15
|
+
## Checks
|
|
16
|
+
|
|
17
|
+
Do not classify a project as large from budget or audience alone. Use the page's
|
|
18
|
+
actual operational dependencies. Never infer permits, insurance, contracts, or
|
|
19
|
+
supplier confirmation.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Rehearsal
|
|
2
|
+
|
|
3
|
+
## Working unit
|
|
4
|
+
|
|
5
|
+
One OneNote page describing one rehearsal or a defined rehearsal period.
|
|
6
|
+
|
|
7
|
+
## Expected evidence
|
|
8
|
+
|
|
9
|
+
Date, time, location, participants, repertoire, goals, equipment, preparation,
|
|
10
|
+
access details, and unresolved musical or logistical questions.
|
|
11
|
+
|
|
12
|
+
## Workflow
|
|
13
|
+
|
|
14
|
+
Identify the single missing fact or preparation step most likely to block the
|
|
15
|
+
rehearsal. When coordination is needed, draft a factual confirmation message
|
|
16
|
+
and audit it before presenting it.
|
|
17
|
+
|
|
18
|
+
## Checks
|
|
19
|
+
|
|
20
|
+
Never infer attendance, venue access, instrument transport, repertoire
|
|
21
|
+
readiness, or schedule compatibility from silence.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Studio Session
|
|
2
|
+
|
|
3
|
+
## Working unit
|
|
4
|
+
|
|
5
|
+
One OneNote page describing one recording session or linked block of sessions.
|
|
6
|
+
|
|
7
|
+
## Expected evidence
|
|
8
|
+
|
|
9
|
+
Dates, studio, engineer, musicians, repertoire, technical requirements,
|
|
10
|
+
deliverables, budget, file handoff, approvals, and payment responsibilities.
|
|
11
|
+
|
|
12
|
+
## Workflow
|
|
13
|
+
|
|
14
|
+
Find the first missing prerequisite for a productive session. Prefer one action
|
|
15
|
+
covering availability, repertoire, personnel, studio, technical preparation,
|
|
16
|
+
or delivery. Draft outreach only from recorded contacts and facts.
|
|
17
|
+
|
|
18
|
+
## Checks
|
|
19
|
+
|
|
20
|
+
Never assume availability, booking, rights clearance, rates, file delivery, or
|
|
21
|
+
approval. Mark absent fields as unknown rather than turning them into tasks with
|
|
22
|
+
invented owners or dates.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Archivist
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Find the smallest set of OneNote pages that answers a focused question.
|
|
6
|
+
|
|
7
|
+
## Method
|
|
8
|
+
|
|
9
|
+
1. Shortlist pages using title, section, and modification date.
|
|
10
|
+
2. Read the primary working-unit page first.
|
|
11
|
+
3. Read comparison pages only when the workflow needs precedent.
|
|
12
|
+
4. Return facts, contradictions, gaps, and exact source-page titles.
|
|
13
|
+
|
|
14
|
+
## Boundaries
|
|
15
|
+
|
|
16
|
+
- A title is not evidence for page contents.
|
|
17
|
+
- Do not read broadly when a focused page is sufficient.
|
|
18
|
+
- Do not claim exhaustive research unless every plausible page was checked.
|
|
19
|
+
- Do not retain or reproduce irrelevant private material.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Auditor
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Check a proposed answer or draft against the OneNote evidence before the user
|
|
6
|
+
acts on it.
|
|
7
|
+
|
|
8
|
+
## Method
|
|
9
|
+
|
|
10
|
+
1. Check every name, date, venue, amount, status, and historical claim.
|
|
11
|
+
2. Separate verified facts from inference and suggested language.
|
|
12
|
+
3. Return `changes required` with exact corrections, or `pass` with sources.
|
|
13
|
+
4. Recheck the corrected version rather than assuming the fix was made.
|
|
14
|
+
|
|
15
|
+
## Boundaries
|
|
16
|
+
|
|
17
|
+
- Independence is analytical, not a separate autonomous process.
|
|
18
|
+
- Style preferences do not override factual accuracy.
|
|
19
|
+
- Never pass unsupported claims such as “as last year.”
|
|
20
|
+
- Never send or write the approved result anywhere outside chat.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Envoy
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Draft concise outreach or coordination copy from verified OneNote facts.
|
|
6
|
+
|
|
7
|
+
## Method
|
|
8
|
+
|
|
9
|
+
1. Use only facts supplied by the current page and Registrar evidence.
|
|
10
|
+
2. Match the relationship and purpose without pretending familiarity.
|
|
11
|
+
3. Leave unknown logistics as explicit placeholders or questions.
|
|
12
|
+
4. Return a subject and editable body in chat.
|
|
13
|
+
|
|
14
|
+
## Boundaries
|
|
15
|
+
|
|
16
|
+
- Draft only; never send.
|
|
17
|
+
- Never claim a prior booking, conversation, or agreement without evidence.
|
|
18
|
+
- Never include irrelevant private context.
|
|
19
|
+
- Always hand the draft to the Auditor before presenting it as ready.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Janitor
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Identify working units or tasks that appear complete, expired, duplicated, or
|
|
6
|
+
superseded and summarize the cleanup a human may choose to perform.
|
|
7
|
+
|
|
8
|
+
## Method
|
|
9
|
+
|
|
10
|
+
1. Read statuses and dates from the relevant page.
|
|
11
|
+
2. Distinguish explicit completion from age-based inference.
|
|
12
|
+
3. Return a compact proposed cleanup list with source evidence.
|
|
13
|
+
|
|
14
|
+
## Boundaries
|
|
15
|
+
|
|
16
|
+
- Never delete, archive, close, or update anything.
|
|
17
|
+
- Never infer completion from an old date alone.
|
|
18
|
+
- Keep the output in chat and state the exact human action required.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Orchestrator
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Turn the user's request and live OneNote evidence into one useful next action.
|
|
6
|
+
|
|
7
|
+
## Method
|
|
8
|
+
|
|
9
|
+
1. Use `list_notes` to locate the most likely working-unit page.
|
|
10
|
+
2. Read that page before selecting a workflow.
|
|
11
|
+
3. Load the closest project type and the smallest necessary set of roles.
|
|
12
|
+
4. Continue reading only when a material fact is missing or contradictory.
|
|
13
|
+
5. Present one recommendation or one finished draft in chat.
|
|
14
|
+
|
|
15
|
+
## Boundaries
|
|
16
|
+
|
|
17
|
+
- Never create a backlog dump when one action can move the project.
|
|
18
|
+
- Never write to OneNote or another service.
|
|
19
|
+
- Never send a message or imply that one was sent.
|
|
20
|
+
- Ask before switching to a different working-unit page.
|
|
21
|
+
- Close the loop in chat with evidence, result, and the next human decision.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Project Manager
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Compare one working-unit page with its project-type playbook and identify the
|
|
6
|
+
single most useful next project-management action.
|
|
7
|
+
|
|
8
|
+
## Method
|
|
9
|
+
|
|
10
|
+
1. Parse the page's facts, milestones, tasks, owners, dates, and statuses.
|
|
11
|
+
2. Respect completed work and current dependencies.
|
|
12
|
+
3. Flag overdue, contradictory, unowned, or unscheduled work.
|
|
13
|
+
4. Recommend one next action with rationale and source evidence.
|
|
14
|
+
|
|
15
|
+
## Boundaries
|
|
16
|
+
|
|
17
|
+
- Never change a status or deadline.
|
|
18
|
+
- Never treat a template date as a real commitment.
|
|
19
|
+
- Do not fabricate dependencies, budgets, availability, or progress.
|
|
20
|
+
- The result exists only in chat for the musician to accept or adapt.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Registrar
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Extract the people, organizations, roles, and contact details required for the
|
|
6
|
+
current action.
|
|
7
|
+
|
|
8
|
+
## Method
|
|
9
|
+
|
|
10
|
+
1. Read the current working-unit page and any directly relevant precedent.
|
|
11
|
+
2. Return the exact recorded name, role, contact value, and source page.
|
|
12
|
+
3. Identify conflicts or stale-looking values without silently correcting them.
|
|
13
|
+
|
|
14
|
+
## Boundaries
|
|
15
|
+
|
|
16
|
+
- Never invent or enrich a contact from outside OneNote.
|
|
17
|
+
- Never contact anyone.
|
|
18
|
+
- If two pages disagree, show both values and ask the user which is current.
|
|
19
|
+
- Minimize exposure: return only contacts relevant to the current action.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Artist Operations
|
|
2
|
+
|
|
3
|
+
One OneNote page is one working unit. Use the Artist Notes MCP tools and the
|
|
4
|
+
playbooks exposed by `list_agent_workflows` and `load_agent_workflow`.
|
|
5
|
+
|
|
6
|
+
Start with `role:orchestrator`. It selects one useful next action, not a task
|
|
7
|
+
dump. Load the matching project type and only the narrow roles needed to finish
|
|
8
|
+
that action.
|
|
9
|
+
|
|
10
|
+
Load `policy:local-state` before persisting agent-owned working context. Local
|
|
11
|
+
state is optional, disposable, and confined to `.artist/local/`. It is not a
|
|
12
|
+
coordination, claims, review, concurrency, or maintenance system.
|
|
13
|
+
|
|
14
|
+
The MVP is strictly read-only:
|
|
15
|
+
|
|
16
|
+
- OneNote is the sole source of artist and project data.
|
|
17
|
+
- `list_notes` and `read_note` are the only data tools.
|
|
18
|
+
- Drafts, audits, plans, and completion summaries appear only in chat.
|
|
19
|
+
- Never send, book, update, close, or write anything on the user's behalf.
|
|
20
|
+
- Cite page titles for material facts and label inference explicitly.
|
|
21
|
+
- Treat missing data as unknown, never as permission or availability.
|
|
22
|
+
- The Auditor reviews the current draft in chat; the Janitor only proposes
|
|
23
|
+
cleanup. Neither role creates infrastructure or performs external writes.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"entries": [
|
|
4
|
+
{
|
|
5
|
+
"id": "policy:local-state",
|
|
6
|
+
"kind": "policy",
|
|
7
|
+
"name": "Local State",
|
|
8
|
+
"description": "Use local state only when it helps the musician resume work without rereading",
|
|
9
|
+
"file": ".artist/policies/LOCAL_STATE.md",
|
|
10
|
+
"sha256": "f29aab016151d8f42fabe5f573622bb79b81c292c3601919ed0bfc839bbbe975"
|
|
11
|
+
},
|
|
12
|
+
{
|
|
13
|
+
"id": "project-type:concert",
|
|
14
|
+
"kind": "project-type",
|
|
15
|
+
"name": "Concert",
|
|
16
|
+
"description": "One OneNote page describing one concert. Expected fields include name, date,",
|
|
17
|
+
"file": ".artist/project-types/CONCERT.md",
|
|
18
|
+
"sha256": "84f45b97be57cc8335e460b2095eb3759bc354d62d50a691317351e93cf6faec"
|
|
19
|
+
},
|
|
20
|
+
{
|
|
21
|
+
"id": "project-type:large-concert",
|
|
22
|
+
"kind": "project-type",
|
|
23
|
+
"name": "Large Concert or Festival",
|
|
24
|
+
"description": "One OneNote page for a concert whose evidence shows additional production",
|
|
25
|
+
"file": ".artist/project-types/LARGE_CONCERT.md",
|
|
26
|
+
"sha256": "7d9811604cf1f2ff5222de650996be4d465d7e5872beb55e89ca1d2bda64eb51"
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"id": "project-type:rehearsal",
|
|
30
|
+
"kind": "project-type",
|
|
31
|
+
"name": "Rehearsal",
|
|
32
|
+
"description": "One OneNote page describing one rehearsal or a defined rehearsal period.",
|
|
33
|
+
"file": ".artist/project-types/REHEARSAL.md",
|
|
34
|
+
"sha256": "4e1de55e2bbee3f163ae1915e1d42b0c706f7cc3abe18bf9ca0cd14ff2c3f979"
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
"id": "project-type:studio-session",
|
|
38
|
+
"kind": "project-type",
|
|
39
|
+
"name": "Studio Session",
|
|
40
|
+
"description": "One OneNote page describing one recording session or linked block of sessions.",
|
|
41
|
+
"file": ".artist/project-types/STUDIO_SESSION.md",
|
|
42
|
+
"sha256": "ba6bf155f31fad12f727cc0a09ea1799efb4d94a70471c5de2f7cfca53926738"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"id": "role:archivist",
|
|
46
|
+
"kind": "role",
|
|
47
|
+
"name": "Archivist",
|
|
48
|
+
"description": "Find the smallest set of OneNote pages that answers a focused question.",
|
|
49
|
+
"file": ".artist/roles/ARCHIVIST.md",
|
|
50
|
+
"sha256": "c9deb6aa6a87367a27b7a6368f2ce461c41583564f441f885246055f07309152"
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
"id": "role:auditor",
|
|
54
|
+
"kind": "role",
|
|
55
|
+
"name": "Auditor",
|
|
56
|
+
"description": "Check a proposed answer or draft against the OneNote evidence before the user",
|
|
57
|
+
"file": ".artist/roles/AUDITOR.md",
|
|
58
|
+
"sha256": "a73a51fc16ec141f6abacf383147e71476d28016a02ba0233add1aa6e038d26b"
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
"id": "role:envoy",
|
|
62
|
+
"kind": "role",
|
|
63
|
+
"name": "Envoy",
|
|
64
|
+
"description": "Draft concise outreach or coordination copy from verified OneNote facts.",
|
|
65
|
+
"file": ".artist/roles/ENVOY.md",
|
|
66
|
+
"sha256": "4989a3fbd1be1c4c08d9046a9eace3d847f9aa27fb5583b1271c158d4576363c"
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"id": "role:janitor",
|
|
70
|
+
"kind": "role",
|
|
71
|
+
"name": "Janitor",
|
|
72
|
+
"description": "Identify working units or tasks that appear complete, expired, duplicated, or",
|
|
73
|
+
"file": ".artist/roles/JANITOR.md",
|
|
74
|
+
"sha256": "bb6592fa5955716c3ba1ac0d837291aafd43d6a0ad77ec2063c9e7fc3f6b4e52"
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"id": "role:orchestrator",
|
|
78
|
+
"kind": "role",
|
|
79
|
+
"name": "Orchestrator",
|
|
80
|
+
"description": "Turn the user's request and live OneNote evidence into one useful next action.",
|
|
81
|
+
"file": ".artist/roles/ORCHESTRATOR.md",
|
|
82
|
+
"sha256": "1f56a9998e138a1ad44dc578f1fa86be7d4a91bae7b56707c6d05f720b01425e"
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"id": "role:project-manager",
|
|
86
|
+
"kind": "role",
|
|
87
|
+
"name": "Project Manager",
|
|
88
|
+
"description": "Compare one working-unit page with its project-type playbook and identify the",
|
|
89
|
+
"file": ".artist/roles/PROJECT_MANAGER.md",
|
|
90
|
+
"sha256": "00cb754ccda592a2fd0a0ac42be8c8ec1d9d06f8f028dcac175e9a2ef1567197"
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
"id": "role:registrar",
|
|
94
|
+
"kind": "role",
|
|
95
|
+
"name": "Registrar",
|
|
96
|
+
"description": "Extract the people, organizations, roles, and contact details required for the",
|
|
97
|
+
"file": ".artist/roles/REGISTRAR.md",
|
|
98
|
+
"sha256": "c9968a574dbe16aa412993569795c9f96c015562e3f7735072a8afe918d96db2"
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
}
|
package/dist/agents.js
ADDED
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { access, copyFile, mkdir, readFile } from 'node:fs/promises';
|
|
3
|
+
import { dirname, isAbsolute, relative, resolve } from 'node:path';
|
|
4
|
+
import { fileURLToPath } from 'node:url';
|
|
5
|
+
const packRoot = fileURLToPath(new URL('../agent-pack/', import.meta.url));
|
|
6
|
+
const defaultRegistryUrl = 'https://raw.githubusercontent.com/ManudotaORG/artist-mcp/main/apps/mcp/agent-pack/registry.json';
|
|
7
|
+
const exists = async (path) => {
|
|
8
|
+
try {
|
|
9
|
+
await access(path);
|
|
10
|
+
return true;
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
const assertSafePackPath = (file) => {
|
|
17
|
+
const path = resolve(packRoot, file);
|
|
18
|
+
const pathFromRoot = relative(packRoot, path);
|
|
19
|
+
if (pathFromRoot.startsWith('..') || isAbsolute(pathFromRoot)) {
|
|
20
|
+
throw new Error(`Agent registry path escapes the pack: ${file}`);
|
|
21
|
+
}
|
|
22
|
+
return path;
|
|
23
|
+
};
|
|
24
|
+
const parseRegistry = (value) => {
|
|
25
|
+
if (!value || typeof value !== 'object') {
|
|
26
|
+
throw new Error('Agent registry is not an object.');
|
|
27
|
+
}
|
|
28
|
+
const candidate = value;
|
|
29
|
+
if (candidate.schemaVersion !== 1 || !Array.isArray(candidate.entries)) {
|
|
30
|
+
throw new Error('Unsupported agent registry format.');
|
|
31
|
+
}
|
|
32
|
+
for (const entry of candidate.entries) {
|
|
33
|
+
if (!entry ||
|
|
34
|
+
typeof entry.id !== 'string' ||
|
|
35
|
+
(entry.kind !== 'role' && entry.kind !== 'project-type' && entry.kind !== 'policy') ||
|
|
36
|
+
typeof entry.name !== 'string' ||
|
|
37
|
+
typeof entry.description !== 'string' ||
|
|
38
|
+
typeof entry.file !== 'string' ||
|
|
39
|
+
!/^[a-f0-9]{64}$/.test(entry.sha256)) {
|
|
40
|
+
throw new Error('Agent registry contains an invalid entry.');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return candidate;
|
|
44
|
+
};
|
|
45
|
+
const readBundledRegistry = async () => {
|
|
46
|
+
const raw = await readFile(resolve(packRoot, 'registry.json'), 'utf8');
|
|
47
|
+
return parseRegistry(JSON.parse(raw));
|
|
48
|
+
};
|
|
49
|
+
const fetchRemoteRegistry = async () => {
|
|
50
|
+
const url = new URL(process.env.ARTIST_MCP_REGISTRY_URL ?? defaultRegistryUrl);
|
|
51
|
+
const response = await fetch(url, { signal: AbortSignal.timeout(3_000) });
|
|
52
|
+
if (!response.ok) {
|
|
53
|
+
throw new Error(`Agent registry returned HTTP ${response.status}.`);
|
|
54
|
+
}
|
|
55
|
+
return { registry: parseRegistry(await response.json()), url };
|
|
56
|
+
};
|
|
57
|
+
const listAgentWorkflows = async () => {
|
|
58
|
+
try {
|
|
59
|
+
return (await fetchRemoteRegistry()).registry.entries;
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return (await readBundledRegistry()).entries;
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
const verifyContent = (entry, content) => {
|
|
66
|
+
const digest = createHash('sha256').update(content).digest('hex');
|
|
67
|
+
if (digest !== entry.sha256) {
|
|
68
|
+
throw new Error(`Checksum verification failed for ${entry.id}.`);
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
const loadBundledWorkflow = async (id) => {
|
|
72
|
+
const registry = await readBundledRegistry();
|
|
73
|
+
const entry = registry.entries.find((item) => item.id === id);
|
|
74
|
+
if (!entry) {
|
|
75
|
+
throw new Error(`Unknown agent workflow: ${id}`);
|
|
76
|
+
}
|
|
77
|
+
const content = await readFile(assertSafePackPath(entry.file), 'utf8');
|
|
78
|
+
verifyContent(entry, content);
|
|
79
|
+
return { ...entry, content };
|
|
80
|
+
};
|
|
81
|
+
const loadAgentWorkflow = async (id) => {
|
|
82
|
+
try {
|
|
83
|
+
const { registry, url } = await fetchRemoteRegistry();
|
|
84
|
+
const entry = registry.entries.find((item) => item.id === id);
|
|
85
|
+
if (!entry) {
|
|
86
|
+
throw new Error(`Unknown agent workflow: ${id}`);
|
|
87
|
+
}
|
|
88
|
+
const response = await fetch(new URL(entry.file, url), {
|
|
89
|
+
signal: AbortSignal.timeout(3_000),
|
|
90
|
+
});
|
|
91
|
+
if (!response.ok) {
|
|
92
|
+
throw new Error(`Agent workflow returned HTTP ${response.status}.`);
|
|
93
|
+
}
|
|
94
|
+
const content = await response.text();
|
|
95
|
+
verifyContent(entry, content);
|
|
96
|
+
return { ...entry, content };
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
try {
|
|
100
|
+
return await loadBundledWorkflow(id);
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
throw error;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
const installAgentPack = async (directory = process.cwd()) => {
|
|
108
|
+
const registry = await readBundledRegistry();
|
|
109
|
+
const packFiles = ['AGENTS.md', ...registry.entries.map((entry) => entry.file)];
|
|
110
|
+
const destinationRoot = resolve(directory);
|
|
111
|
+
const conflicts = [];
|
|
112
|
+
let agentsInstructionsSkipped = false;
|
|
113
|
+
for (const file of packFiles) {
|
|
114
|
+
const source = assertSafePackPath(file);
|
|
115
|
+
const destination = resolve(destinationRoot, file);
|
|
116
|
+
const pathFromRoot = relative(destinationRoot, destination);
|
|
117
|
+
if (pathFromRoot.startsWith('..') || isAbsolute(pathFromRoot)) {
|
|
118
|
+
throw new Error(`Agent pack target escapes the destination: ${file}`);
|
|
119
|
+
}
|
|
120
|
+
if (await exists(destination)) {
|
|
121
|
+
const [current, bundled] = await Promise.all([
|
|
122
|
+
readFile(destination, 'utf8'),
|
|
123
|
+
readFile(source, 'utf8'),
|
|
124
|
+
]);
|
|
125
|
+
if (current !== bundled) {
|
|
126
|
+
if (file === 'AGENTS.md')
|
|
127
|
+
agentsInstructionsSkipped = true;
|
|
128
|
+
else
|
|
129
|
+
conflicts.push(file);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (conflicts.length > 0) {
|
|
134
|
+
throw new Error(`Agent pack stopped before writing. Existing files differ: ${conflicts.join(', ')}`);
|
|
135
|
+
}
|
|
136
|
+
for (const file of packFiles) {
|
|
137
|
+
const destination = resolve(destinationRoot, file);
|
|
138
|
+
if (file === 'AGENTS.md' && agentsInstructionsSkipped) {
|
|
139
|
+
console.log('Left existing AGENTS.md unchanged');
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
await mkdir(dirname(destination), { recursive: true });
|
|
143
|
+
if (!(await exists(destination))) {
|
|
144
|
+
await copyFile(assertSafePackPath(file), destination);
|
|
145
|
+
console.log(`Installed ${file}`);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
console.log(`Already current ${file}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (agentsInstructionsSkipped) {
|
|
152
|
+
console.log('Reference the files under .artist/ from your existing AGENTS.md.');
|
|
153
|
+
}
|
|
154
|
+
console.log('Read-only artist workflow pack is ready.');
|
|
155
|
+
};
|
|
156
|
+
export { installAgentPack, listAgentWorkflows, loadAgentWorkflow, };
|
package/dist/client.js
CHANGED
|
@@ -7,9 +7,9 @@
|
|
|
7
7
|
*/
|
|
8
8
|
const DEFAULT_ENDPOINT = "https://zxiemadwrkcoovvpscfb.supabase.co/functions/v1/graph";
|
|
9
9
|
/** Overridable for testing; the baked-in default is what shipped copies use. */
|
|
10
|
-
export
|
|
10
|
+
export const endpoint = () => {
|
|
11
11
|
return process.env.ARTIST_MCP_ENDPOINT ?? DEFAULT_ENDPOINT;
|
|
12
|
-
}
|
|
12
|
+
};
|
|
13
13
|
export class GraphError extends Error {
|
|
14
14
|
reconnectNeeded;
|
|
15
15
|
constructor(message, reconnectNeeded) {
|
|
@@ -18,7 +18,7 @@ export class GraphError extends Error {
|
|
|
18
18
|
this.name = "GraphError";
|
|
19
19
|
}
|
|
20
20
|
}
|
|
21
|
-
export async
|
|
21
|
+
export const call = async (op, key, params = {}) => {
|
|
22
22
|
let res;
|
|
23
23
|
try {
|
|
24
24
|
res = await fetch(endpoint(), {
|
|
@@ -43,4 +43,4 @@ export async function call(op, key, params = {}) {
|
|
|
43
43
|
throw new GraphError(detail, body.reconnect_needed === true);
|
|
44
44
|
}
|
|
45
45
|
return body;
|
|
46
|
-
}
|
|
46
|
+
};
|
package/dist/config.js
CHANGED
|
@@ -4,7 +4,7 @@ import { join } from "node:path";
|
|
|
4
4
|
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
5
5
|
import { dirname } from "node:path";
|
|
6
6
|
export const ENTRY_NAME = "artist-notes";
|
|
7
|
-
export
|
|
7
|
+
export const configPath = () => {
|
|
8
8
|
const home = homedir();
|
|
9
9
|
switch (platform()) {
|
|
10
10
|
case "darwin":
|
|
@@ -17,9 +17,9 @@ export function configPath() {
|
|
|
17
17
|
throw new Error(`Claude Desktop has no config location on ${platform()}. ` +
|
|
18
18
|
"Install on macOS or Windows.");
|
|
19
19
|
}
|
|
20
|
-
}
|
|
20
|
+
};
|
|
21
21
|
/** Missing file is not an error — it's the first-install case. */
|
|
22
|
-
export async
|
|
22
|
+
export const readConfig = async (path) => {
|
|
23
23
|
let raw;
|
|
24
24
|
try {
|
|
25
25
|
raw = await readFile(path, "utf8");
|
|
@@ -38,8 +38,8 @@ export async function readConfig(path) {
|
|
|
38
38
|
throw new Error(`${path} is not valid JSON. Fix or move it, then run init again — ` +
|
|
39
39
|
"refusing to overwrite a file that may hold your other MCP servers.");
|
|
40
40
|
}
|
|
41
|
-
}
|
|
42
|
-
export async
|
|
41
|
+
};
|
|
42
|
+
export const writeConfig = async (path, config) => {
|
|
43
43
|
await mkdir(dirname(path), { recursive: true });
|
|
44
44
|
await writeFile(path, `${JSON.stringify(config, null, 2)}\n`, "utf8");
|
|
45
|
-
}
|
|
45
|
+
};
|
package/dist/index.js
CHANGED
|
@@ -1,23 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { runInit, runUninstall } from
|
|
3
|
-
import { runServer } from
|
|
2
|
+
import { runInit, runUninstall } from './init.js';
|
|
3
|
+
import { runServer } from './server.js';
|
|
4
|
+
import { installAgentPack } from './agents.js';
|
|
4
5
|
const mode = process.argv[2];
|
|
5
6
|
try {
|
|
6
7
|
switch (mode) {
|
|
7
8
|
case undefined:
|
|
8
9
|
await runServer();
|
|
9
10
|
break;
|
|
10
|
-
case
|
|
11
|
+
case 'init':
|
|
11
12
|
await runInit();
|
|
12
13
|
break;
|
|
13
|
-
case
|
|
14
|
+
case 'uninstall':
|
|
14
15
|
await runUninstall();
|
|
15
16
|
break;
|
|
17
|
+
case 'agents':
|
|
18
|
+
if (process.argv[3] !== 'install') {
|
|
19
|
+
throw new Error('Usage: artist-mcp agents install [directory]');
|
|
20
|
+
}
|
|
21
|
+
await installAgentPack(process.argv[4]);
|
|
22
|
+
break;
|
|
16
23
|
default:
|
|
17
24
|
console.error(`Unknown command "${mode}".\n\n` +
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
25
|
+
' artist-mcp run the MCP server over stdio\n' +
|
|
26
|
+
' artist-mcp init connect this machine to your notes\n' +
|
|
27
|
+
' artist-mcp agents install [directory]\n' +
|
|
28
|
+
' install the notes-analysis agent pack\n' +
|
|
29
|
+
' artist-mcp uninstall remove the Claude Desktop entry\n');
|
|
21
30
|
process.exit(1);
|
|
22
31
|
}
|
|
23
32
|
}
|
package/dist/init.js
CHANGED
|
@@ -3,7 +3,7 @@ import { stdin, stdout } from "node:process";
|
|
|
3
3
|
import { call, GraphError } from "./client.js";
|
|
4
4
|
import { configPath, ENTRY_NAME, readConfig, writeConfig } from "./config.js";
|
|
5
5
|
const PACKAGE = "@manudota/artist-mcp";
|
|
6
|
-
export async
|
|
6
|
+
export const runInit = async () => {
|
|
7
7
|
const rl = createInterface({ input: stdin, output: stdout });
|
|
8
8
|
let key;
|
|
9
9
|
try {
|
|
@@ -38,8 +38,8 @@ export async function runInit() {
|
|
|
38
38
|
await writeConfig(path, config);
|
|
39
39
|
console.log(`\nConnected. Wrote "${ENTRY_NAME}" to ${path}`);
|
|
40
40
|
console.log("Restart Claude Desktop — it doesn't reload its config on its own.");
|
|
41
|
-
}
|
|
42
|
-
export async
|
|
41
|
+
};
|
|
42
|
+
export const runUninstall = async () => {
|
|
43
43
|
const path = configPath();
|
|
44
44
|
const config = await readConfig(path);
|
|
45
45
|
const servers = (config.mcpServers ?? {});
|
|
@@ -52,4 +52,4 @@ export async function runUninstall() {
|
|
|
52
52
|
await writeConfig(path, config);
|
|
53
53
|
console.log(`Removed "${ENTRY_NAME}" from ${path}`);
|
|
54
54
|
console.log("Restart Claude Desktop to apply.");
|
|
55
|
-
}
|
|
55
|
+
};
|
package/dist/server.js
CHANGED
|
@@ -1,15 +1,47 @@
|
|
|
1
1
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
2
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
3
|
import { z } from "zod";
|
|
4
|
+
import { listAgentWorkflows, loadAgentWorkflow } from "./agents.js";
|
|
4
5
|
import { call, GraphError } from "./client.js";
|
|
5
|
-
|
|
6
|
+
const serverVersion = '0.2.0'; // x-release-please-version
|
|
7
|
+
const errorResult = (err) => {
|
|
8
|
+
const message = err instanceof GraphError ? err.message : `Unexpected error: ${err}`;
|
|
9
|
+
return { content: [{ type: "text", text: message }], isError: true };
|
|
10
|
+
};
|
|
11
|
+
const runServer = async () => {
|
|
6
12
|
const key = process.env.ARTIST_MCP_KEY;
|
|
7
13
|
if (!key) {
|
|
8
14
|
// stderr, not stdout — stdout is the protocol channel.
|
|
9
15
|
console.error("ARTIST_MCP_KEY is not set. Run `npx @manudota/artist-mcp init` to configure.");
|
|
10
16
|
process.exit(1);
|
|
11
17
|
}
|
|
12
|
-
const server = new McpServer({ name: "artist-notes", version:
|
|
18
|
+
const server = new McpServer({ name: "artist-notes", version: serverVersion });
|
|
19
|
+
server.tool("list_agent_workflows", "List the read-only artist roles, project types, and policies available at runtime. Load the Orchestrator before handling a project.", {}, async () => {
|
|
20
|
+
try {
|
|
21
|
+
const entries = await listAgentWorkflows();
|
|
22
|
+
const lines = entries.map((entry) => `- ${entry.id}: ${entry.name} — ${entry.description}`);
|
|
23
|
+
return { content: [{ type: "text", text: lines.join("\n") }] };
|
|
24
|
+
}
|
|
25
|
+
catch (err) {
|
|
26
|
+
return errorResult(err);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
server.tool("load_agent_workflow", "Load one checksummed role, project-type, or policy playbook. This only returns instructions; it never changes OneNote or another service.", { workflow_id: z.string().describe("An id returned by list_agent_workflows") }, async ({ workflow_id }) => {
|
|
30
|
+
try {
|
|
31
|
+
const workflow = await loadAgentWorkflow(workflow_id);
|
|
32
|
+
return {
|
|
33
|
+
content: [
|
|
34
|
+
{
|
|
35
|
+
type: "text",
|
|
36
|
+
text: `# ${workflow.name}\n\n${workflow.content}`,
|
|
37
|
+
},
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
catch (err) {
|
|
42
|
+
return errorResult(err);
|
|
43
|
+
}
|
|
44
|
+
});
|
|
13
45
|
server.tool("list_notes", "List the user's OneNote pages, with title, section and last modified date.", {}, async () => {
|
|
14
46
|
try {
|
|
15
47
|
const { notes } = await call("list_notes", key);
|
|
@@ -33,8 +65,5 @@ export async function runServer() {
|
|
|
33
65
|
}
|
|
34
66
|
});
|
|
35
67
|
await server.connect(new StdioServerTransport());
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const message = err instanceof GraphError ? err.message : `Unexpected error: ${err}`;
|
|
39
|
-
return { content: [{ type: "text", text: message }], isError: true };
|
|
40
|
-
}
|
|
68
|
+
};
|
|
69
|
+
export { runServer };
|
package/package.json
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@manudota/artist-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "MCP server that reads your OneNote notes, via a hosted connection.",
|
|
5
|
+
"repository": {
|
|
6
|
+
"type": "git",
|
|
7
|
+
"url": "https://github.com/ManudotaORG/artist-mcp"
|
|
8
|
+
},
|
|
5
9
|
"type": "module",
|
|
6
10
|
"bin": {
|
|
7
11
|
"artist-mcp": "dist/index.js"
|
|
8
12
|
},
|
|
9
13
|
"files": [
|
|
10
|
-
"dist"
|
|
14
|
+
"dist",
|
|
15
|
+
"agent-pack"
|
|
11
16
|
],
|
|
12
17
|
"engines": {
|
|
13
18
|
"node": ">=20"
|
|
@@ -16,7 +21,8 @@
|
|
|
16
21
|
"access": "public"
|
|
17
22
|
},
|
|
18
23
|
"scripts": {
|
|
19
|
-
"build": "tsc -p tsconfig.json && chmod +x dist/index.js",
|
|
24
|
+
"build": "node scripts/build-agent-registry.mjs && tsc -p tsconfig.json && chmod +x dist/index.js",
|
|
25
|
+
"test": "pnpm run build && node --test test/*.test.mjs",
|
|
20
26
|
"prepublishOnly": "pnpm run build"
|
|
21
27
|
},
|
|
22
28
|
"dependencies": {
|