stellar-agent 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +68 -1
- package/package.json +12 -3
- package/src/core-skills/module-help.csv +2 -0
- package/src/core-skills/stellar-help/SKILL.md +5 -0
- package/src/core-skills/stellar-lean/SKILL.md +70 -0
- package/src/core-skills/stellar-party-mode/SKILL.md +2 -1
- package/src/core-skills/stellar-project-context/SKILL.md +152 -0
- package/src/stellar-skills/1-analysis/stellar-agent-analyst/customize.toml +5 -0
- package/src/stellar-skills/1-analysis/stellar-known-assets/SKILL.md +125 -0
- package/src/stellar-skills/4-implementation/stellar-agent-devops/customize.toml +5 -0
- package/src/stellar-skills/4-implementation/stellar-doctor/SKILL.md +207 -0
- package/src/stellar-skills/5-mentorship/stellar-agent-mentor/SKILL.md +60 -0
- package/src/stellar-skills/5-mentorship/stellar-agent-mentor/customize.toml +56 -0
- package/src/stellar-skills/5-mentorship/stellar-demo-prep/SKILL.md +139 -0
- package/src/stellar-skills/5-mentorship/stellar-idea-scoping/SKILL.md +125 -0
- package/src/stellar-skills/5-mentorship/stellar-judging-criteria/SKILL.md +119 -0
- package/src/stellar-skills/5-mentorship/stellar-mvp-definition/SKILL.md +124 -0
- package/src/stellar-skills/5-mentorship/stellar-pitch-coaching/SKILL.md +150 -0
- package/src/stellar-skills/5-mentorship/stellar-submission-prep/SKILL.md +234 -0
- package/src/stellar-skills/5-mentorship/stellar-track-recommendation/SKILL.md +97 -0
- package/src/stellar-skills/module-help.csv +9 -0
- package/src/stellar-skills/module.yaml +7 -0
- package/tools/installer/commands/init.js +31 -2
- package/tools/installer/commands/mcp.js +38 -0
- package/tools/installer/commands/version.js +42 -0
- package/tools/installer/stellar-cli.js +1 -1
- package/tools/mcp-server/server.js +281 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stellar-track-recommendation
|
|
3
|
+
description: 'Match a hackathon idea to the right Stellar/Soroban track and surface the judging rubric for that track. Use when the user is undecided between tracks, wants to know which track fits their idea best, or needs to read the rubric before building.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Track Recommendation
|
|
7
|
+
|
|
8
|
+
## Purpose
|
|
9
|
+
|
|
10
|
+
Pick the right hackathon track for the idea. The wrong track punishes the team twice — judges score against criteria the build wasn't optimized for, and the team misses the rubric that would've shaped a better build.
|
|
11
|
+
|
|
12
|
+
## On Activation
|
|
13
|
+
|
|
14
|
+
Load `{planning_artifacts}/hackathon-brief.md` if it exists. If not, recommend running `IS` (Idea Scoping) first.
|
|
15
|
+
|
|
16
|
+
If a hackathon URL was provided to Pera on greeting, fetch it now and read the official track list and rubrics. Treat the live page as source of truth — overrides anything below.
|
|
17
|
+
|
|
18
|
+
## Workflow
|
|
19
|
+
|
|
20
|
+
### Step 1: Identify Candidate Tracks
|
|
21
|
+
|
|
22
|
+
Match the idea against the Stellar hackathon track families:
|
|
23
|
+
|
|
24
|
+
| Track | Best fits | Stellar/Soroban primitive | Judging weight |
|
|
25
|
+
|---|---|---|---|
|
|
26
|
+
| **DeFi** | Lending, AMM, yield, perp/swap, stablecoin mech | Soroban contracts, AMM pools, trustlines | Technical depth · Composability · TVL potential |
|
|
27
|
+
| **Payments** | Cross-border, remittance, micropayments, payroll | Classic Stellar payments, anchors, path payments | UX · Cost per tx · Anchor / SEP integration |
|
|
28
|
+
| **Identity / SEP-10** | KYC, attestation, reputation, gated access | SEP-10 auth, Soroban storage for attestations | Privacy · SEP compliance · Real-world fit |
|
|
29
|
+
| **Gaming / NFT** | On-chain assets, in-game economy, collectibles | Custom assets, Soroban inventory contracts | Player retention · Creative use of Soroban |
|
|
30
|
+
| **RWA / Tokenization** | Real estate, invoices, commodities, carbon | Custom assets + Soroban escrow + oracles | Legal viability · Oracle design · Trust model |
|
|
31
|
+
| **Infra / DevTools** | SDKs, dashboards, oracles, indexers | Anything that helps other devs ship on Stellar | Developer adoption · Reusability |
|
|
32
|
+
| **Open / Other** | Idea doesn't fit above | Anything | Innovation · Demo · Business potential |
|
|
33
|
+
|
|
34
|
+
### Step 2: Score the Idea Against Each Candidate
|
|
35
|
+
|
|
36
|
+
For each candidate track, produce a 3-line block:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
<Track>
|
|
40
|
+
Fit: <high | medium | low> — <one-line reason>
|
|
41
|
+
Win condition: <what would the judges score highly>
|
|
42
|
+
Risk: <what the judges will challenge>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
If two tracks score `high`, prefer the one with:
|
|
46
|
+
- Smaller judging field (less competition)
|
|
47
|
+
- Rubric the build naturally aligns to without rework
|
|
48
|
+
|
|
49
|
+
### Step 3: Recommend
|
|
50
|
+
|
|
51
|
+
Output:
|
|
52
|
+
|
|
53
|
+
```markdown
|
|
54
|
+
## Track recommendation
|
|
55
|
+
|
|
56
|
+
**Primary:** <track>
|
|
57
|
+
**Why:** <one sentence — the strongest rubric alignment>
|
|
58
|
+
**Risk to mitigate:** <what to harden before submission>
|
|
59
|
+
|
|
60
|
+
**Secondary (if primary doesn't fit at submission):** <track>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Step 4: Pull the Rubric
|
|
64
|
+
|
|
65
|
+
If the hackathon URL is known, extract the official rubric from the page. Otherwise use the generic Stellar judging rubric:
|
|
66
|
+
|
|
67
|
+
| Criterion | Default weight | Notes |
|
|
68
|
+
|---|---|---|
|
|
69
|
+
| Innovation | 25% | "Have we seen this before on Stellar?" |
|
|
70
|
+
| Technical implementation | 25% | Soroban depth, code quality, test coverage |
|
|
71
|
+
| Business / impact | 25% | Who pays, how big, distribution path |
|
|
72
|
+
| Demo quality | 25% | Recording, narration, on-chain proof |
|
|
73
|
+
|
|
74
|
+
Append to `{planning_artifacts}/hackathon-brief.md` as a `## Track + rubric` section.
|
|
75
|
+
|
|
76
|
+
### Step 5: Confirm
|
|
77
|
+
|
|
78
|
+
Ask the user: *"Locking in `<track>` — proceed?"*
|
|
79
|
+
|
|
80
|
+
If yes, suggest `MV` next.
|
|
81
|
+
If no, present the secondary or ask what's missing.
|
|
82
|
+
|
|
83
|
+
## Common mistakes
|
|
84
|
+
|
|
85
|
+
- **Picking DeFi by default.** DeFi is the most crowded track at most Stellar hackathons. A "DeFi-lite" idea in Payments or Identity often wins more easily.
|
|
86
|
+
- **Picking a track because the team likes it.** Track choice is about rubric fit, not preference.
|
|
87
|
+
- **Ignoring the official rubric.** Always pull the actual hackathon's rubric over generic priors. Hackathons weight criteria differently.
|
|
88
|
+
- **Multi-track submissions.** Most Stellar hackathons only allow one track. Picking two splits attention.
|
|
89
|
+
|
|
90
|
+
## Output
|
|
91
|
+
|
|
92
|
+
Updates `{planning_artifacts}/hackathon-brief.md` with the chosen track and the rubric the build must align to.
|
|
93
|
+
|
|
94
|
+
## Next Steps
|
|
95
|
+
|
|
96
|
+
- `stellar-mvp-definition` (MV) — cut the feature list against the chosen rubric
|
|
97
|
+
- `stellar-judging-criteria` (JC) — when the MVP is locked, map it to the rubric line-by-line
|
|
@@ -3,11 +3,13 @@ Stellar,_meta,,,,,,,,,false,https://github.com/tolgazorlu/stellar-agent#readme,
|
|
|
3
3
|
Stellar,stellar-analytics,Analytics Setup,AN,Set up dbt blockchain analytics for Stellar network data and tokenomics reporting.,,,1-analysis,,,false,{project_knowledge},dbt models
|
|
4
4
|
Stellar,stellar-market-research,Market Research,MR,DeFi landscape competitive analysis and Stellar ecosystem research.,,,1-analysis,,,false,{planning_artifacts},research document
|
|
5
5
|
Stellar,stellar-domain-research,Domain Research,DR,Stellar protocol deep dive — tokenomics regulatory landscape and use case research.,,,1-analysis,,,false,{planning_artifacts},research document
|
|
6
|
+
Stellar,stellar-known-assets,Known Assets Registry,KA,Look up canonical Stellar asset issuer addresses (USDC EURC AQUA yXLM) with provenance and verification commands. Prevents issuer hallucination.,,,1-analysis,,,false,,
|
|
6
7
|
Stellar,stellar-project-brief,Project Brief,PB,Define the dApp concept goals and target users in a concise brief.,,,2-planning,,,false,{planning_artifacts},project-brief
|
|
7
8
|
Stellar,stellar-prd,Stellar dApp PRD,PR,Create or update Product Requirements Document for a Stellar application.,,,2-planning,stellar-project-brief,,true,{planning_artifacts},prd
|
|
8
9
|
Stellar,stellar-epics-stories,Epics and Stories,ES,Break PRD into development epics and user stories with Stellar-specific acceptance criteria.,,,2-planning,stellar-prd,,true,{planning_artifacts},epics-stories
|
|
9
10
|
Stellar,stellar-architecture-doc,Architecture Document,AD,Create technical architecture document covering on-chain vs off-chain Soroban contract design and Horizon integration.,,,3-architecture,stellar-prd,,true,{planning_artifacts},architecture
|
|
10
11
|
Stellar,stellar-setup-environment,Setup Environment,SE,Set up local Stellar development environment with Docker quickstart Stellar CLI and SDK.,,,4-implementation,,,true,{project_knowledge},environment-guide
|
|
12
|
+
Stellar,stellar-doctor,Stellar Doctor,SD,Diagnose toolchain network reachability wallet state contract state and project context. Run before any deployment.,,,4-implementation,,,false,,
|
|
11
13
|
Stellar,stellar-init-contract,Init Contract,IC,Initialize a new Soroban smart contract project with Rust workspace and scaffold.,,,4-implementation,stellar-setup-environment,,false,{implementation_artifacts},contract scaffold
|
|
12
14
|
Stellar,stellar-write-contract,Write Contract,WC,Implement Soroban contract logic including storage types host functions and error handling.,,,4-implementation,stellar-init-contract,,false,{implementation_artifacts},contract code
|
|
13
15
|
Stellar,stellar-test-contract,Test Contract,TC,Write and run comprehensive Soroban contract tests using the Rust test harness.,,,4-implementation,stellar-write-contract,,true,{implementation_artifacts},test suite
|
|
@@ -22,3 +24,10 @@ Stellar,stellar-nextjs-wallet,Next.js Wallet,NW,Integrate Freighter wallet into
|
|
|
22
24
|
Stellar,stellar-nextjs-soroban,Next.js Soroban,NS,Call Soroban contracts from Next.js using Server Components API routes and client-side signing.,,,4-implementation,stellar-nextjs-wallet,,false,{implementation_artifacts},soroban-integration
|
|
23
25
|
Stellar,stellar-freighter-integration,Freighter Integration,FI,Deep Freighter wallet integration — full API reference connection lifecycle and blob signing.,,,4-implementation,stellar-setup-environment,,false,{implementation_artifacts},freighter-integration
|
|
24
26
|
Stellar,stellar-sep10-auth,SEP-10 Auth,S10,Implement SEP-10 Web Authentication with JWT for Next.js API routes and protected endpoints.,,,4-implementation,stellar-nextjs-setup,,false,{implementation_artifacts},sep10-auth
|
|
27
|
+
Stellar,stellar-idea-scoping,Idea Scoping,IS,Pressure-test the hackathon idea against the build window and produce a one-page brief with cut list.,,,5-mentorship,,,false,{planning_artifacts},hackathon-brief
|
|
28
|
+
Stellar,stellar-track-recommendation,Track Recommendation,TR,Match the idea to the right Stellar/Soroban hackathon track and pull the judging rubric.,,,5-mentorship,stellar-idea-scoping,,false,{planning_artifacts},track-decision
|
|
29
|
+
Stellar,stellar-mvp-definition,MVP Definition,MV,Split features into must-have / nice-to-have / skip with hour estimates and a 25% buffer rule.,,,5-mentorship,stellar-idea-scoping,,false,{planning_artifacts},mvp-cut
|
|
30
|
+
Stellar,stellar-judging-criteria,Judging Criteria,JC,Reverse-map the build to the judging rubric and identify gaps before they cost points.,,,5-mentorship,stellar-mvp-definition,,false,{planning_artifacts},rubric-alignment
|
|
31
|
+
Stellar,stellar-demo-prep,Demo Prep,DM,Build the hackathon demo storyboard 2-3 min script and recording checklist with common failure modes.,,,5-mentorship,stellar-mvp-definition,,false,{implementation_artifacts},demo-storyboard
|
|
32
|
+
Stellar,stellar-pitch-coaching,Pitch Coaching,PC,Build the hackathon pitch 3-minute deck outline elevator pitch and judge Q&A drill.,,,5-mentorship,stellar-demo-prep,,false,{planning_artifacts},pitch-script
|
|
33
|
+
Stellar,stellar-submission-prep,Submission Prep,SP,Final pre-submission checklist repo polish README demo video deployed testnet contract IDs and submission form.,,,5-mentorship,stellar-pitch-coaching,,false,{planning_artifacts},submission-confirmation
|
|
@@ -101,3 +101,10 @@ agents:
|
|
|
101
101
|
icon: "🎨"
|
|
102
102
|
team: stellar-development
|
|
103
103
|
description: "Builds Next.js App Router dApps connected to the Stellar network. Expert in Freighter wallet integration, Soroban contract calls from React, Horizon data fetching in Server Components, and SEP-10 authentication. Knows exactly when to use Server vs Client Components and never exposes private keys."
|
|
104
|
+
|
|
105
|
+
- code: stellar-agent-mentor
|
|
106
|
+
name: Pera
|
|
107
|
+
title: Hackathon Mentor
|
|
108
|
+
icon: "🧭"
|
|
109
|
+
team: stellar-development
|
|
110
|
+
description: "Hackathon-arc mentor who guides teams from idea to demo. Scopes ideas to fit hackathon timeboxes, picks the right Stellar/Soroban track, aligns the build to judging criteria, and coaches demo + pitch. Speaks in checklists and time-boxed cuts — every recommendation tied to the submission deadline."
|
|
@@ -54,6 +54,7 @@ async function installStellarAgent(targetDir, config) {
|
|
|
54
54
|
planning_artifacts: `${OUTPUT_DIR}/planning-artifacts`,
|
|
55
55
|
implementation_artifacts: `${OUTPUT_DIR}/implementation-artifacts`,
|
|
56
56
|
project_knowledge: 'docs',
|
|
57
|
+
skills_version: config.skills_version,
|
|
57
58
|
};
|
|
58
59
|
await fsp.writeFile(
|
|
59
60
|
path.join(stellarDir, 'stellar', 'config.yaml'),
|
|
@@ -64,6 +65,7 @@ async function installStellarAgent(targetDir, config) {
|
|
|
64
65
|
// Write manifest
|
|
65
66
|
const manifest = {
|
|
66
67
|
version: require('../../../package.json').version,
|
|
68
|
+
skills_version: config.skills_version,
|
|
67
69
|
installed_at: new Date().toISOString(),
|
|
68
70
|
project_name: config.project_name,
|
|
69
71
|
};
|
|
@@ -209,6 +211,15 @@ icon = "🎨"
|
|
|
209
211
|
team = "stellar-development"
|
|
210
212
|
module = "stellar"
|
|
211
213
|
description = "Builds Next.js App Router dApps connected to Stellar — Freighter wallet, Soroban contract calls, SEP-10 auth."
|
|
214
|
+
|
|
215
|
+
[[agents]]
|
|
216
|
+
code = "stellar-agent-mentor"
|
|
217
|
+
name = "Pera"
|
|
218
|
+
title = "Hackathon Mentor"
|
|
219
|
+
icon = "🧭"
|
|
220
|
+
team = "stellar-development"
|
|
221
|
+
module = "stellar"
|
|
222
|
+
description = "Hackathon mentor — scopes ideas to the timebox, picks the track, aligns to judging criteria, coaches demo and pitch."
|
|
212
223
|
`;
|
|
213
224
|
|
|
214
225
|
await fsp.writeFile(path.join(stellarDir, 'config.toml'), tomlContent, 'utf8');
|
|
@@ -217,8 +228,23 @@ description = "Builds Next.js App Router dApps connected to Stellar — Freighte
|
|
|
217
228
|
}
|
|
218
229
|
|
|
219
230
|
async function action(options) {
|
|
231
|
+
const pkgVersion = require('../../../package.json').version;
|
|
232
|
+
const requestedVersion = options?.skills || pkgVersion;
|
|
233
|
+
|
|
234
|
+
// Pinning warning — we don't ship multi-version download support yet
|
|
235
|
+
if (requestedVersion !== pkgVersion) {
|
|
236
|
+
console.error(
|
|
237
|
+
`⚠️ Skills version mismatch.\n` +
|
|
238
|
+
` You requested: ${requestedVersion}\n` +
|
|
239
|
+
` This package: ${pkgVersion}\n\n` +
|
|
240
|
+
` To pin to a different version, run:\n` +
|
|
241
|
+
` npx stellar-agent@${requestedVersion} init\n\n` +
|
|
242
|
+
` Continuing with ${pkgVersion}...\n`
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
220
246
|
await CLIUtils.displayLogo();
|
|
221
|
-
await prompts.intro(
|
|
247
|
+
await prompts.intro(`Stellar Agent v${pkgVersion} — AI agent team for Stellar blockchain development`);
|
|
222
248
|
|
|
223
249
|
// Directory selection
|
|
224
250
|
const rawDir = await prompts.text({
|
|
@@ -298,6 +324,7 @@ async function action(options) {
|
|
|
298
324
|
network_preference: networkPreference,
|
|
299
325
|
primary_language: primaryLanguage,
|
|
300
326
|
communication_language: communicationLanguage,
|
|
327
|
+
skills_version: pkgVersion,
|
|
301
328
|
};
|
|
302
329
|
|
|
303
330
|
// Install
|
|
@@ -330,6 +357,8 @@ async function action(options) {
|
|
|
330
357
|
module.exports = {
|
|
331
358
|
command: 'init',
|
|
332
359
|
description: 'Initialize Stellar Agent in your project',
|
|
333
|
-
options: [
|
|
360
|
+
options: [
|
|
361
|
+
['--skills <version>', 'Pin to a specific skills version (defaults to the installed package version)'],
|
|
362
|
+
],
|
|
334
363
|
action,
|
|
335
364
|
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `stellar-agent mcp` — start the MCP server over stdio.
|
|
3
|
+
*
|
|
4
|
+
* Invoked by MCP-aware clients (Claude Desktop, Cline, Continue, Zed)
|
|
5
|
+
* via their server config:
|
|
6
|
+
*
|
|
7
|
+
* {
|
|
8
|
+
* "mcpServers": {
|
|
9
|
+
* "stellar-agent": {
|
|
10
|
+
* "command": "npx",
|
|
11
|
+
* "args": ["stellar-agent", "mcp"]
|
|
12
|
+
* }
|
|
13
|
+
* }
|
|
14
|
+
* }
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const path = require('node:path');
|
|
18
|
+
const { spawn } = require('node:child_process');
|
|
19
|
+
|
|
20
|
+
async function action() {
|
|
21
|
+
const serverPath = path.join(__dirname, '..', '..', 'mcp-server', 'server.js');
|
|
22
|
+
const child = spawn(process.execPath, [serverPath], {
|
|
23
|
+
stdio: 'inherit',
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
child.on('exit', (code) => process.exit(code ?? 0));
|
|
27
|
+
child.on('error', (err) => {
|
|
28
|
+
console.error('Failed to start MCP server:', err);
|
|
29
|
+
process.exit(1);
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
module.exports = {
|
|
34
|
+
command: 'mcp',
|
|
35
|
+
description: 'Run the Stellar Agent MCP server (stdio). Used by Claude Desktop, Cline, Continue, Zed.',
|
|
36
|
+
options: [],
|
|
37
|
+
action,
|
|
38
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `stellar-agent version` — print CLI version + installed skills version (if any).
|
|
3
|
+
*
|
|
4
|
+
* Outputs both:
|
|
5
|
+
* - the version of this CLI / npm package
|
|
6
|
+
* - the skills version recorded at `_stellar/_config/manifest.yaml`
|
|
7
|
+
* if the current directory has stellar-agent installed
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('node:fs');
|
|
11
|
+
const path = require('node:path');
|
|
12
|
+
const yaml = require('yaml');
|
|
13
|
+
|
|
14
|
+
async function action() {
|
|
15
|
+
const pkg = require('../../../package.json');
|
|
16
|
+
console.log(`stellar-agent v${pkg.version}`);
|
|
17
|
+
|
|
18
|
+
const manifestPath = path.join(process.cwd(), '_stellar', '_config', 'manifest.yaml');
|
|
19
|
+
if (fs.existsSync(manifestPath)) {
|
|
20
|
+
try {
|
|
21
|
+
const manifest = yaml.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
22
|
+
console.log(` installed: ${manifest.installed_at}`);
|
|
23
|
+
console.log(` skills version: ${manifest.skills_version || manifest.version || '(unknown)'}`);
|
|
24
|
+
console.log(` project: ${manifest.project_name || '(unset)'}`);
|
|
25
|
+
if (manifest.skills_version && manifest.skills_version !== pkg.version) {
|
|
26
|
+
console.log('');
|
|
27
|
+
console.log(` ⚠️ CLI (${pkg.version}) differs from installed skills (${manifest.skills_version}).`);
|
|
28
|
+
console.log(` Run \`npx stellar-agent init\` (choose Update) to refresh, or`);
|
|
29
|
+
console.log(` run \`npx stellar-agent@${manifest.skills_version} <command>\` to stay pinned.`);
|
|
30
|
+
}
|
|
31
|
+
} catch (err) {
|
|
32
|
+
console.error(` (could not parse manifest: ${err.message})`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
module.exports = {
|
|
38
|
+
command: 'version',
|
|
39
|
+
description: 'Print the CLI version and the installed skills version (if any)',
|
|
40
|
+
options: [],
|
|
41
|
+
action,
|
|
42
|
+
};
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Stellar Agent — MCP Server
|
|
4
|
+
*
|
|
5
|
+
* Exposes the stellar-agent skill catalog and agent personas to any
|
|
6
|
+
* MCP-compatible client (Claude Desktop, Cline, Continue, Zed, etc.)
|
|
7
|
+
* over stdio.
|
|
8
|
+
*
|
|
9
|
+
* Hand-rolled JSON-RPC 2.0 implementation to avoid an SDK dependency —
|
|
10
|
+
* keeps the published package lean.
|
|
11
|
+
*
|
|
12
|
+
* Tools exposed:
|
|
13
|
+
* - list_agents → roster of all 8 agents
|
|
14
|
+
* - list_skills(phase?, agent?) → catalog of skills
|
|
15
|
+
* - get_skill(name) → SKILL.md content
|
|
16
|
+
* - get_agent(name) → persona block + menu
|
|
17
|
+
*
|
|
18
|
+
* Run: `stellar-agent mcp` or `node tools/mcp-server/server.js`
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
const fsp = require('node:fs/promises');
|
|
22
|
+
const fs = require('node:fs');
|
|
23
|
+
const path = require('node:path');
|
|
24
|
+
const readline = require('node:readline');
|
|
25
|
+
const yaml = require('yaml');
|
|
26
|
+
|
|
27
|
+
const REPO_ROOT = path.join(__dirname, '..', '..');
|
|
28
|
+
const SRC_DIR = path.join(REPO_ROOT, 'src');
|
|
29
|
+
const STELLAR_SKILLS_DIR = path.join(SRC_DIR, 'stellar-skills');
|
|
30
|
+
const CORE_SKILLS_DIR = path.join(SRC_DIR, 'core-skills');
|
|
31
|
+
const PROTOCOL_VERSION = '2024-11-05';
|
|
32
|
+
const SERVER_INFO = { name: 'stellar-agent', version: require('../../package.json').version };
|
|
33
|
+
|
|
34
|
+
// ---------- Catalog loaders ----------
|
|
35
|
+
|
|
36
|
+
async function loadAgents() {
|
|
37
|
+
const moduleYaml = await fsp.readFile(path.join(STELLAR_SKILLS_DIR, 'module.yaml'), 'utf8');
|
|
38
|
+
const data = yaml.parse(moduleYaml);
|
|
39
|
+
return data.agents || [];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function loadSkills({ phase, agent } = {}) {
|
|
43
|
+
const skills = [];
|
|
44
|
+
|
|
45
|
+
// Walk core-skills/*/SKILL.md
|
|
46
|
+
for (const entry of await fsp.readdir(CORE_SKILLS_DIR, { withFileTypes: true })) {
|
|
47
|
+
if (!entry.isDirectory()) continue;
|
|
48
|
+
const skillPath = path.join(CORE_SKILLS_DIR, entry.name, 'SKILL.md');
|
|
49
|
+
if (!fs.existsSync(skillPath)) continue;
|
|
50
|
+
skills.push(await readSkillFrontmatter(skillPath, 'core', entry.name));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Walk stellar-skills/<phase>/<skill>/SKILL.md
|
|
54
|
+
for (const phaseEntry of await fsp.readdir(STELLAR_SKILLS_DIR, { withFileTypes: true })) {
|
|
55
|
+
if (!phaseEntry.isDirectory()) continue;
|
|
56
|
+
const phaseDir = path.join(STELLAR_SKILLS_DIR, phaseEntry.name);
|
|
57
|
+
for (const skillEntry of await fsp.readdir(phaseDir, { withFileTypes: true })) {
|
|
58
|
+
if (!skillEntry.isDirectory()) continue;
|
|
59
|
+
const skillPath = path.join(phaseDir, skillEntry.name, 'SKILL.md');
|
|
60
|
+
if (!fs.existsSync(skillPath)) continue;
|
|
61
|
+
skills.push(await readSkillFrontmatter(skillPath, phaseEntry.name, skillEntry.name));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let filtered = skills;
|
|
66
|
+
if (phase) filtered = filtered.filter((s) => s.phase === phase);
|
|
67
|
+
if (agent) filtered = filtered.filter((s) => (s.agent || '').toLowerCase() === agent.toLowerCase());
|
|
68
|
+
return filtered;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function readSkillFrontmatter(filePath, phase, dirname) {
|
|
72
|
+
const raw = await fsp.readFile(filePath, 'utf8');
|
|
73
|
+
const frontmatter = parseFrontmatter(raw);
|
|
74
|
+
// Detect agent-persona skills by naming convention: stellar-agent-*
|
|
75
|
+
const isAgent = dirname.startsWith('stellar-agent-');
|
|
76
|
+
return {
|
|
77
|
+
name: frontmatter.name || dirname,
|
|
78
|
+
description: frontmatter.description || '',
|
|
79
|
+
phase,
|
|
80
|
+
type: isAgent ? 'agent' : 'skill',
|
|
81
|
+
path: path.relative(REPO_ROOT, filePath),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function parseFrontmatter(text) {
|
|
86
|
+
const match = text.match(/^---\n([\s\S]*?)\n---/);
|
|
87
|
+
if (!match) return {};
|
|
88
|
+
const out = {};
|
|
89
|
+
for (const line of match[1].split('\n')) {
|
|
90
|
+
const m = line.match(/^([a-zA-Z_]+):\s*['"]?(.*?)['"]?$/);
|
|
91
|
+
if (m) out[m[1]] = m[2];
|
|
92
|
+
}
|
|
93
|
+
return out;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function getSkillContent(name) {
|
|
97
|
+
// Look in core-skills
|
|
98
|
+
const corePath = path.join(CORE_SKILLS_DIR, name, 'SKILL.md');
|
|
99
|
+
if (fs.existsSync(corePath)) return fsp.readFile(corePath, 'utf8');
|
|
100
|
+
// Look in stellar-skills/<phase>/<name>
|
|
101
|
+
for (const phaseEntry of await fsp.readdir(STELLAR_SKILLS_DIR, { withFileTypes: true })) {
|
|
102
|
+
if (!phaseEntry.isDirectory()) continue;
|
|
103
|
+
const candidate = path.join(STELLAR_SKILLS_DIR, phaseEntry.name, name, 'SKILL.md');
|
|
104
|
+
if (fs.existsSync(candidate)) return fsp.readFile(candidate, 'utf8');
|
|
105
|
+
}
|
|
106
|
+
throw new Error(`Skill not found: ${name}`);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function getAgentDetail(name) {
|
|
110
|
+
const agents = await loadAgents();
|
|
111
|
+
const agent = agents.find(
|
|
112
|
+
(a) =>
|
|
113
|
+
a.code === name ||
|
|
114
|
+
a.name?.toLowerCase() === name.toLowerCase() ||
|
|
115
|
+
a.code === `stellar-agent-${name.toLowerCase()}`
|
|
116
|
+
);
|
|
117
|
+
if (!agent) throw new Error(`Agent not found: ${name}`);
|
|
118
|
+
|
|
119
|
+
// Resolve agent's SKILL.md + customize.toml for menu
|
|
120
|
+
let menu = [];
|
|
121
|
+
let persona = null;
|
|
122
|
+
for (const phaseEntry of await fsp.readdir(STELLAR_SKILLS_DIR, { withFileTypes: true })) {
|
|
123
|
+
if (!phaseEntry.isDirectory()) continue;
|
|
124
|
+
const dir = path.join(STELLAR_SKILLS_DIR, phaseEntry.name, agent.code);
|
|
125
|
+
if (!fs.existsSync(dir)) continue;
|
|
126
|
+
const skillPath = path.join(dir, 'SKILL.md');
|
|
127
|
+
const customizePath = path.join(dir, 'customize.toml');
|
|
128
|
+
if (fs.existsSync(skillPath)) persona = await fsp.readFile(skillPath, 'utf8');
|
|
129
|
+
if (fs.existsSync(customizePath)) {
|
|
130
|
+
const tomlText = await fsp.readFile(customizePath, 'utf8');
|
|
131
|
+
menu = parseAgentMenu(tomlText);
|
|
132
|
+
}
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
return { agent, persona, menu };
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function parseAgentMenu(toml) {
|
|
139
|
+
// Lightweight extractor for [[agent.menu]] blocks
|
|
140
|
+
const blocks = toml.split(/\[\[agent\.menu\]\]/).slice(1);
|
|
141
|
+
return blocks.map((block) => {
|
|
142
|
+
const lines = block.split('\n');
|
|
143
|
+
const entry = {};
|
|
144
|
+
for (const line of lines) {
|
|
145
|
+
const m = line.match(/^\s*(code|description|skill)\s*=\s*"(.*)"\s*$/);
|
|
146
|
+
if (m) entry[m[1]] = m[2];
|
|
147
|
+
}
|
|
148
|
+
return entry;
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// ---------- MCP protocol ----------
|
|
153
|
+
|
|
154
|
+
const TOOLS = [
|
|
155
|
+
{
|
|
156
|
+
name: 'list_agents',
|
|
157
|
+
description: 'List all Stellar Agent team members with their handle, role, icon, and phase.',
|
|
158
|
+
inputSchema: { type: 'object', properties: {}, additionalProperties: false },
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
name: 'list_skills',
|
|
162
|
+
description:
|
|
163
|
+
'List available skills in the Stellar Agent catalog. Optionally filter by phase (1-analysis, 2-planning, 3-architecture, 4-implementation, 5-mentorship, core).',
|
|
164
|
+
inputSchema: {
|
|
165
|
+
type: 'object',
|
|
166
|
+
properties: {
|
|
167
|
+
phase: {
|
|
168
|
+
type: 'string',
|
|
169
|
+
description: 'Optional phase filter — e.g. "4-implementation" or "core".',
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
additionalProperties: false,
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
name: 'get_skill',
|
|
177
|
+
description: 'Return the full SKILL.md content for a named skill (e.g. "stellar-write-contract").',
|
|
178
|
+
inputSchema: {
|
|
179
|
+
type: 'object',
|
|
180
|
+
properties: { name: { type: 'string', description: 'Skill name (kebab-case).' } },
|
|
181
|
+
required: ['name'],
|
|
182
|
+
additionalProperties: false,
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
name: 'get_agent',
|
|
187
|
+
description:
|
|
188
|
+
'Return the persona, principles, and skill menu for a specific Stellar Agent (e.g. "sol", "pera").',
|
|
189
|
+
inputSchema: {
|
|
190
|
+
type: 'object',
|
|
191
|
+
properties: {
|
|
192
|
+
name: { type: 'string', description: 'Agent name or handle (e.g. "sol" or "stellar-agent-developer").' },
|
|
193
|
+
},
|
|
194
|
+
required: ['name'],
|
|
195
|
+
additionalProperties: false,
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
];
|
|
199
|
+
|
|
200
|
+
function jsonResponse(id, result) {
|
|
201
|
+
return JSON.stringify({ jsonrpc: '2.0', id, result });
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function jsonError(id, code, message) {
|
|
205
|
+
return JSON.stringify({ jsonrpc: '2.0', id, error: { code, message } });
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
async function handleRequest(req) {
|
|
209
|
+
const { id, method, params } = req;
|
|
210
|
+
|
|
211
|
+
if (method === 'initialize') {
|
|
212
|
+
return jsonResponse(id, {
|
|
213
|
+
protocolVersion: PROTOCOL_VERSION,
|
|
214
|
+
capabilities: { tools: {} },
|
|
215
|
+
serverInfo: SERVER_INFO,
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (method === 'tools/list') {
|
|
220
|
+
return jsonResponse(id, { tools: TOOLS });
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
if (method === 'tools/call') {
|
|
224
|
+
const { name, arguments: args = {} } = params || {};
|
|
225
|
+
try {
|
|
226
|
+
let payload;
|
|
227
|
+
if (name === 'list_agents') {
|
|
228
|
+
payload = await loadAgents();
|
|
229
|
+
} else if (name === 'list_skills') {
|
|
230
|
+
payload = await loadSkills(args);
|
|
231
|
+
} else if (name === 'get_skill') {
|
|
232
|
+
if (!args.name) throw new Error('Missing required argument: name');
|
|
233
|
+
payload = await getSkillContent(args.name);
|
|
234
|
+
} else if (name === 'get_agent') {
|
|
235
|
+
if (!args.name) throw new Error('Missing required argument: name');
|
|
236
|
+
payload = await getAgentDetail(args.name);
|
|
237
|
+
} else {
|
|
238
|
+
return jsonError(id, -32601, `Unknown tool: ${name}`);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const text = typeof payload === 'string' ? payload : JSON.stringify(payload, null, 2);
|
|
242
|
+
return jsonResponse(id, { content: [{ type: 'text', text }] });
|
|
243
|
+
} catch (err) {
|
|
244
|
+
return jsonResponse(id, {
|
|
245
|
+
content: [{ type: 'text', text: `Error: ${err.message}` }],
|
|
246
|
+
isError: true,
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (method === 'notifications/initialized') {
|
|
252
|
+
return null; // no response for notifications
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return jsonError(id, -32601, `Method not found: ${method}`);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
// ---------- Stdio loop ----------
|
|
259
|
+
|
|
260
|
+
async function main() {
|
|
261
|
+
const rl = readline.createInterface({ input: process.stdin });
|
|
262
|
+
rl.on('line', async (line) => {
|
|
263
|
+
if (!line.trim()) return;
|
|
264
|
+
let req;
|
|
265
|
+
try {
|
|
266
|
+
req = JSON.parse(line);
|
|
267
|
+
} catch (err) {
|
|
268
|
+
process.stdout.write(jsonError(null, -32700, 'Parse error') + '\n');
|
|
269
|
+
return;
|
|
270
|
+
}
|
|
271
|
+
const response = await handleRequest(req);
|
|
272
|
+
if (response) process.stdout.write(response + '\n');
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
rl.on('close', () => process.exit(0));
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
main().catch((err) => {
|
|
279
|
+
console.error('MCP server fatal error:', err);
|
|
280
|
+
process.exit(1);
|
|
281
|
+
});
|