🎩 You're Invited:Meet the Socket team at Black Hat in Las Vegas, August 3-6.RSVP
Sign In

nakodo

Package Overview
Dependencies
Maintainers
1
Versions
6
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

nakodo - npm Package Compare versions

Comparing version
0.0.1
to
0.1.0
+3
-0
dist/api-client.js

@@ -50,2 +50,5 @@ export class ApiError extends Error {

}
pendingIntros() {
return this.request('GET', '/api/intros/pending');
}
// Telemetry must never break the user experience — swallow all failures.

@@ -52,0 +55,0 @@ async logEvent(type, installId, metadata) {

+43
-15

@@ -24,2 +24,20 @@ import { z } from 'zod';

const NOT_REGISTERED = 'The user has no profile yet. Call find_collaborator with their need to get the onboarding steps.';
// v1.1: the in-session intro channel. Best-effort — a failed check must never
// break the tool call it rides on.
async function pendingNotice() {
try {
const { intros } = await client().pendingIntros();
if (intros.length === 0)
return '';
return [
``,
``,
`🔔 ${intros.length === 1 ? 'An introduction is waiting' : `${intros.length} introductions are waiting`} for the user. Tell them — an anonymous card describing someone worth meeting is ready to accept or decline (the other person learns nothing unless both say yes):`,
...intros.map((i) => ` ${i.url}`),
].join('\n');
}
catch {
return '';
}
}
export function registerTools(server) {

@@ -33,3 +51,4 @@ server.registerTool('find_collaborator', {

'This registers their need with a private matching network — nothing is published, there is no feed or public profile, ' +
'and introductions arrive by email only when both sides accept. If the user has no profile yet, this returns onboarding steps.',
'and an introduction is an anonymous card both sides must accept; contact details are exchanged only by the two people themselves afterwards. ' +
'If the user has no profile yet, this returns onboarding steps.',
inputSchema: {

@@ -51,8 +70,8 @@ need: z

`2. Show the user the draft and revise until they explicitly approve it. Nothing is ever stored without their approval.`,
`3. Ask for their email — frame it as "where your person shows up": the only thing that ever arrives there is an introduction. Optionally a handle/name and location (enables near-you matching).`,
`4. Ask exactly this and record the answer: "How did you find this tool?" (examples: you the agent found it via a tool/registry search, a launch post, a friend). Pass it as \`source\`.`,
`3. Ask exactly this and record the answer: "How did you find this tool?" (examples: you the agent found it via a tool/registry search, a launch post, a friend). Pass it as \`source\`.`,
`4. Optionally: a handle/name and location (enables near-you matching), and — only if they want one — an email address. Be honest about what the email is: purely a heads-up channel to tell them an introduction is waiting. It is never shared with anyone, never shown to a match, and they can skip it entirely — you (the agent) will tell them about waiting introductions in-session instead.`,
`5. Call create_profile with all of the above.`,
`6. Then call find_collaborator again with the same need: ${JSON.stringify(need)}`,
``,
`Worth telling the user: profiles and updates are never shown to anyone — only compared. No feed, no faces. Declining an intro is invisible to the other side. delete_me removes everything, any time.`,
`Worth telling the user: profiles and updates are never shown to anyone — only compared. No feed, no faces. Declining an intro is invisible to the other side. Contact details are exchanged only by the two people themselves, after both accept — never by the platform. delete_me removes everything, any time.`,
].join('\n'));

@@ -63,4 +82,5 @@ }

return text(`Registered as a standing ask: ${JSON.stringify(need)}. The matcher now compares it privately against what other builders are working on. ` +
`When there's a real match, an introduction arrives by email as an anonymous card — no names until both sides accept. ` +
`Silence in the meantime is normal; nothing about this is visible to anyone. The ask stays open until matched.`);
`When there's a real match, an introduction appears as an anonymous card — no names, and nothing happens unless both sides accept. This tool will tell you here when one is waiting. ` +
`Silence in the meantime is normal; nothing about this is visible to anyone. The ask stays open until matched.` +
(await pendingNotice()));
}

@@ -73,7 +93,11 @@ catch (err) {

title: 'Create profile',
description: 'Register the user with the matching network: their agent-drafted, human-approved profile plus their email. ' +
'ONLY call this after the user has explicitly approved the exact profile text and provided their email — ' +
description: 'Register the user with the matching network: their agent-drafted, human-approved profile. ' +
'ONLY call this after the user has explicitly approved the exact profile text — ' +
'never with unapproved or inferred content. Usually called during the onboarding flow started by find_collaborator.',
inputSchema: {
email: z.string().email().describe('Where introductions arrive. Provided by the user.'),
email: z
.string()
.email()
.optional()
.describe('Optional. Used only to notify the user that an introduction is waiting — never shared with anyone, never shown to a match. Only include if the user offered it.'),
profile: z.string().min(1).max(10_000).describe('The profile text, exactly as approved by the user.'),

@@ -96,5 +120,8 @@ source: z

const { token } = await api.register({ email, handle, location, source, install_id: cfg.install_id });
saveConfig({ ...cfg, email, token });
saveConfig({ ...cfg, ...(email ? { email } : {}), token });
await new ApiClient(apiUrl(), token).saveProfile(profile);
return text(`Profile is on record and ${email} is set as where introductions arrive (a welcome email is on its way). ` +
return text(`Profile is on record. ` +
(email
? `${email} is set as the notification channel — the only thing that ever arrives there is a heads-up that an introduction is waiting (a welcome email is on its way). `
: `No email on record — introductions will be announced right here in-session instead. `) +
`Reassure the user: the profile is never displayed to anyone — only compared, privately, to find their person. ` +

@@ -105,3 +132,3 @@ `If there was a pending need, call find_collaborator with it now.`);

if (err instanceof ApiError && err.status === 409) {
return errorText(`That email already has a record (likely from another machine). ${err.body?.hint ?? ''} Tell the user.`);
return errorText(`That email already has a record (likely from another machine). ${err.body?.hint ?? ''} Tell the user — or onboard without an email; it's optional.`);
}

@@ -126,3 +153,4 @@ return handleApiError(err);

await client().addSnippet(snippet);
return text('On record. Never displayed, only compared — it just made their next match a little sharper.');
return text('On record. Never displayed, only compared — it just made their next match a little sharper.' +
(await pendingNotice()));
}

@@ -146,3 +174,3 @@ catch (err) {

``,
`Email: ${record.user.email}${record.user.handle ? ` · Handle: ${record.user.handle}` : ''}${record.user.location ? ` · Location: ${record.user.location}` : ''}`,
`Email: ${record.user.email ?? '(none — introductions are announced here in-session)'}${record.user.handle ? ` · Handle: ${record.user.handle}` : ''}${record.user.location ? ` · Location: ${record.user.location}` : ''}`,
``,

@@ -157,3 +185,3 @@ `Profile:`,

...record.snippets.map((s) => `- [${String(s.created_at).slice(0, 10)}] ${s.body}`),
].join('\n'));
].join('\n') + (await pendingNotice()));
}

@@ -160,0 +188,0 @@ catch (err) {

{
"name": "nakodo",
"version": "0.0.1",
"version": "0.1.0",
"description": "Nakodo makes your coding agent your networker: find a collaborator, co-founder, or someone to help with design, code, marketing, or distribution — matched privately on what you're actually building. No feed, no faces; the only output is an introduction.",

@@ -5,0 +5,0 @@ "type": "module",

# Nakodo
Your agent knows what you're building better than anyone. Nakodo makes it your networker: a social network with no feed, no faces, and no performance, that only ever outputs one thing — the right person.
Your agent knows what you're building better than anyone. Nakodo makes it your networker — when you need a co-founder, a collaborator, or someone strong at the thing you're weakest at, your agent finds the person, matched privately on what you're *actually building*, not what you claim.
*Nakōdo (仲人): the traditional Japanese matchmaker — the discreet go-between who knows both sides, and only speaks when there's a real match.*
**Status: pre-release.** This package is reserved while we finish v1. Install instructions, the five trust guarantees, and the first usable release land shortly. Meanwhile: [nakodo.dev](https://nakodo.dev)
No feed. No faces. No performance. The only output is an introduction.
## Install
```
claude mcp add nakodo -- npx -y nakodo
```
Works with Claude Code, Cursor, and any MCP-capable agent (stdio command: `npx -y nakodo`). Then just ask:
> *"find me someone who could help with design"* · *"I'm looking for a co-founder"* · *"find someone building something similar to get feedback from"*
## How it works
1. Your agent drafts a short profile from what it already knows of your work — you approve every word before anything is stored.
2. As you build, it captures short updates (each one approved by you) — a proof-of-work record, not a résumé.
3. When there's a real match, an anonymous card appears: relevant facts, no name, no face. Both sides accept, or nothing happens.
4. After a mutual yes, the intro page becomes your connection — you two exchange whatever contact details you choose. Nothing is ever sent on your behalf.
## Five guarantees
1. Nothing is captured without your explicit, per-snippet approval.
2. Your profile and snippets are never displayed to anyone — only compared.
3. No feed. No browse. The only output is an introduction.
4. Declines are invisible — if either side passes, the other never knows.
5. One command deletes everything: tell your agent *delete me*, and your record is gone.
Email is optional — it's used only to tell you an introduction is waiting, never shared, never shown to a match. Skip it and your agent announces introductions in-session instead.
Data lives in the EU (Frankfurt). [nakodo.dev](https://nakodo.dev) · hello@nakodo.dev