Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
ff8e3b4
package
ForestMars Jul 20, 2026
6f1ba84
mcpServer
ForestMars Jul 20, 2026
b072be9
update mcp server
ForestMars Jul 20, 2026
a6556ad
lock
ForestMars Jul 20, 2026
39ce044
package
ForestMars Jul 20, 2026
15c98d8
mcpserver
ForestMars Jul 20, 2026
ca412e0
tool registry should use inputSchema, not parameters
ForestMars Jul 20, 2026
beaf780
v2 registry version
ForestMars Jul 20, 2026
fd6aa87
registry backup
ForestMars Jul 20, 2026
8102e55
generate registry backup
ForestMars Jul 20, 2026
3d83a1b
mcp server debuggin
ForestMars Jul 20, 2026
c9b0322
MCP v2 has inputSchema not parameters
ForestMars Jul 20, 2026
1e24d3e
update tool name in manifest for MCP v2
ForestMars Jul 20, 2026
0a3b779
update tool name in manifest for MCP v2
ForestMars Jul 20, 2026
88285c7
update tool name in manifest for MCP v2
ForestMars Jul 20, 2026
e9674d1
legacy tools
ForestMars Jul 20, 2026
5c8333f
readme
ForestMars Jul 20, 2026
8008b85
file header
ForestMars Jul 20, 2026
6368a17
file header
ForestMars Jul 20, 2026
d82746c
file header
ForestMars Jul 20, 2026
79312f1
day tool barrel
ForestMars Jul 20, 2026
0a688dd
manifest
ForestMars Jul 20, 2026
6d80339
manifest
ForestMars Jul 20, 2026
3308287
schema
ForestMars Jul 20, 2026
c31021d
test
ForestMars Jul 20, 2026
564a56a
index
ForestMars Jul 20, 2026
a358881
barrel
ForestMars Jul 20, 2026
9928fda
check for broken names on startup
ForestMars Jul 20, 2026
df9f3c5
registry updated
ForestMars Jul 20, 2026
05f1a3e
check for broken names when generating registry
ForestMars Jul 20, 2026
65fa07a
check for broken names when generating registry
ForestMars Jul 20, 2026
f4503cf
report broken tool names at compile time
ForestMars Jul 20, 2026
43e17c9
generate registry
ForestMars Jul 20, 2026
b73360d
handle invalid names gracefully
ForestMars Jul 20, 2026
8409829
manifest
ForestMars Jul 20, 2026
0f06c8a
manifest
ForestMars Jul 20, 2026
736044f
manifest
ForestMars Jul 20, 2026
da0df39
manifest
ForestMars Jul 20, 2026
2516a44
close issue manifest
ForestMars Jul 20, 2026
9f2a74d
Merge branch 'main' into mcp-service
ForestMars Jul 21, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 5 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,19 +26,17 @@
"sup": "AGENT_TYPE=support bun --env-file=.auth apps/cli/chat.ts",
"tui": "TUI_MODE=1 bun --env-file=.auth apps/tui/index.tsx",
"tui:debug": "AGENT_TYPE=coding bun --env-file=.auth apps/tui/index.tsx",

"build": "bun run type-check && vite build",
"build:ci": "bun run build && bun run test:ci",
"build:dev": "vite build --mode development",
"prebuild": "bun scripts/generate-tool-registry.ts",
"preview": "vite preview",

"lint": "eslint . --cache --max-warnings 0",
"lint:fix": "eslint . --cache --fix --max-warnings 0",
"test:bail": "bun test --coverage --bail ./packages/**/*.test.ts",
"test:cov": "bun test --coverage ./src/**/*.test.ts ./tests/**/*.test.ts",
"test:e2e": "bun test ./tests/e2e/e2e.test.ts",
"test:report": "bun test ./tests/unit/*.test.ts --reporter=junit --no-cache --reporter-outfile", // pass [filepath]
"test:report": "bun test ./tests/unit/*.test.ts --reporter=junit --no-cache --reporter-outfile",
"test:watch": "bun test --watch",
"tests": "bun test ./tests/**/*.test.ts",
"tests:int": "bun test ./tests/integration/*.test.ts",
Expand All @@ -49,15 +47,16 @@
"clean:fr": "find . -name 'dist' -type d -prune -exec rm -rf {} + && find . -name '*.tsbuildinfo' -delete && tsc --build --clean",
"clean:nuke": "find . -type f \\( -name '*.js' -o -name '*.d.ts' -o -name '*.d.ts.map' -o -name '*.js.map' -o -name '*.tsbuildinfo' \\) -not -path '*/node_modules/*' -delete && find . -name 'dist' -type d -prune -exec rm -rf {} +",
"reinstall": "bun pm cache rm && rm -rf node_modules && bun install",
"rebuild": "bun run clean:dist && bun run clean:tsbuildinfo && bun tsc --build --force",

"rebuild": "bun run clean:dist && bun run clean:tsbuildinfo && bun tsc --build --force",
"tui:coda": "TUI_MODE=1 AGENT=coding bun --env-file=.auth apps/tui/index.tsx"
},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"dependencies": {
"@cfworker/json-schema": "^4.1.1",
"@modelcontextprotocol/server": "^2.0.0-beta.4",
"@openfeature/core": "1.9.2",
"@openfeature/flagd-provider": "^0.13.4",
"@openfeature/server-sdk": "1.20.2",
Expand All @@ -76,7 +75,7 @@
"pino-loki": "^2.3.0",
"pino-opentelemetry-transport": "^3.0.0",
"posthog-node": "^5.26.2",
"zod": "^4.3.6"
"zod": "^4.4.3"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
Expand Down
36 changes: 36 additions & 0 deletions packages/tools/day-tool/day-server.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
// day-server.mjs
import { WebSocket, WebSocketServer } from 'ws';

const wss = new WebSocketServer({ port: 9001 });

console.log('Day MCP Server running on ws://localhost:9001');

wss.on('connection', (ws) => {
ws.on('message', (data) => {
const msg = JSON.parse(data);

if (msg.method === 'tools/list') {
ws.send(JSON.stringify({
jsonrpc: '2.0',
id: msg.id,
result: {
tools: [{
name: 'get_day',
description: 'Returns current day of the week'
}]
}
}));
}

if (msg.method === 'tools/call' && msg.params.name === 'get_day') {
const day = new Date().toLocaleDateString('en-US', { weekday: 'long' });
ws.send(JSON.stringify({
jsonrpc: '2.0',
id: msg.id,
result: {
content: [{ type: 'text', text: `Today is ${day}` }]
}
}));
}
});
});
22 changes: 22 additions & 0 deletions packages/tools/day-tool/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// packages/tools/day/index.ts
//
// Drop-in MCP v2 tool. Replaces the standalone day-server.mjs, which ran
// its own ws://localhost:9001 JSON-RPC server. Under v2, tools are plain
// functions loaded into the shared mcpServer.ts process — there is no
// per-tool transport, socket, or server instance anymore.
//
// This file is the module's barrel: it's the single import surface for
// the tool (run, inputSchema, and the input type all re-export from here).

import type { DayGetInput } from './schema';

export { inputSchema } from './schema';
export type { DayGetInput } from './schema';

export async function run(_params: DayGetInput): Promise<{ day: string; text: string }> {
const day = new Date().toLocaleDateString('en-US', { weekday: 'long' });
return {
day,
text: `Today is ${day}`
};
}
10 changes: 10 additions & 0 deletions packages/tools/day-tool/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"name": "day.get",
"description": "Returns the current day of the week",
"importPath": "day/index.ts",
"inputSchema": {
"type": "object",
"properties": {},
"additionalProperties": false
}
}
15 changes: 15 additions & 0 deletions packages/tools/day-tool/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// packages/tools/day/schema.ts
//
// JSON Schema (2020-12) for the day.get tool's input. This tool takes no
// arguments today, but the schema is kept explicit rather than omitted so
// fromJsonSchema() in mcpServer.ts has a real object to validate against,
// and so adding an argument later (e.g. `timezone`) is a schema change,
// not a new file.

export const inputSchema = {
type: 'object',
properties: {},
additionalProperties: false
} as const;

export type DayGetInput = Record<string, never>;
78 changes: 78 additions & 0 deletions packages/tools/day-tool/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// packages/tools/day/test.ts
//
// Run with: bun test packages/tools/day/test.ts

import { describe, it, expect, beforeEach, afterEach } from 'bun:test';
import { run } from './index';
import { inputSchema } from './schema';

describe('day.get', () => {
it('returns a weekday name matching the current system date', async () => {
const expected = new Date().toLocaleDateString('en-US', { weekday: 'long' });
const result = await run({});
expect(result.day).toBe(expected);
});

it('returns text in the "Today is <Day>" format', async () => {
const result = await run({});
expect(result.text).toBe(`Today is ${result.day}`);
});

it('returns one of the seven valid weekday names', async () => {
const validDays = [
'Sunday', 'Monday', 'Tuesday', 'Wednesday',
'Thursday', 'Friday', 'Saturday'
];
const result = await run({});
expect(validDays).toContain(result.day);
});

it('ignores unexpected input rather than throwing', async () => {
// The schema declares additionalProperties: false, but that's
// enforced by the MCP server's validator before dispatch, not by
// the tool function itself — the tool should still be defensive.
await expect(run({} as never)).resolves.toBeDefined();
});
});

describe('day.get across known dates', () => {
const realDateNow = Date;

function mockDate(iso: string) {
class MockDate extends Date {
constructor() {
super(iso);
}
}
// @ts-expect-error - intentional global override for testing
global.Date = MockDate;
}

afterEach(() => {
global.Date = realDateNow;
});

it('resolves a Monday correctly', async () => {
mockDate('2026-07-20T12:00:00Z'); // confirmed Monday
const result = await run({});
expect(result.day).toBe('Monday');
expect(result.text).toBe('Today is Monday');
});

it('resolves a Sunday correctly', async () => {
mockDate('2026-07-19T12:00:00Z'); // confirmed Sunday
const result = await run({});
expect(result.day).toBe('Sunday');
});
});

describe('day.get schema', () => {
it('declares an object schema with no required properties', () => {
expect(inputSchema.type).toBe('object');
expect(inputSchema.properties).toEqual({});
});

it('rejects additional properties', () => {
expect(inputSchema.additionalProperties).toBe(false);
});
});
3 changes: 3 additions & 0 deletions packages/tools/email-tool/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
This email tool is a legacy tool that is coupled to its own server, and does not work with CODA2's MCP v2 compatible tool server.

It is not loaded, not run, and should not be used.
16 changes: 16 additions & 0 deletions packages/tools/email-tool/config/contacts.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
computekie@gmail.com <computekie@gmail.com>
Mars Group <themarsgroup@gmail.com>
Gilad Penn <freeslugs@gmail.com>
Hannah Onstad <hannah.onstad@gmail.com>
Isabella Mariè <p8styco@gmail.com>
Jake <jnaviasky@gmail.com>
Peter Kreutlein <pkreutlein@gmail.com>
Natasha Pelak <natashapelak@gmail.com>
Serene <keroserene@gmail.com>
Varun Gangal <vgtomahawk@gmail.com>
Yang Paul <chaoquny0@gmail.com>
Zak <zakhap@gmail.com>
Zoe Braiterman <braiterman.z@gmail.com>
Zeta Jones <lostjournals@gmail.com>
NewYork ctoLunches <newyork@ctolunches.groups.io>

10 changes: 10 additions & 0 deletions packages/tools/email-tool/config/llm_sigs.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"llm_signatures": [
"Written by a language model\nThat I actually own\nWild concept, I know",
"Sent via my personal AI overlord. \n(Yes, a local LLM wrote this. No, I didn't proofread it.)",
"Drafted by silicon, approved by carbon\nRunning locally because I have trust issues",
"Composed by an LLM that doesn't phone home\nUnlike literally everything else you use",
"Sent from my local LLM\nBecause I don't trust yours",
"Sent from my local LLM\n(Yes, it runs on my machine. No, you can't have my data.)"
]
}
10 changes: 10 additions & 0 deletions packages/tools/email-tool/config/sig.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@

Best,

Forest Mars
--
Sent via my personal AI overlord
(Yes, a local LLM wrote this. No, I didn't proofread it.)

<a href="https://compustretch.com">Compustretch: The machine that thinks about doing it for you</a>
Powered by <a href="https://github.com/ForestMars/Polyglot">Polyglot</a>
12 changes: 12 additions & 0 deletions packages/tools/email-tool/config/sig2.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@

Best,

Forest Mars
--
Sent via my personal AI overlord
(Yes, a local LLM wrote this. No, I didn't proofread it.)

My AI Blog: mlops.nyc

<a href="https://compustretch.com">Compustretch: The machine that thinks about doing it for you</a>
Powered by <a href="https://github.com/ForestMars/Polyglot">Polyglot</a>
33 changes: 33 additions & 0 deletions packages/tools/email-tool/config/tool-descript.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
Send an email via Gmail SMTP. Use parameter name "to" for recipient address.

PARAMETERS:
- to: Recipient email address(es). Can be a single email string OR an array of email strings for multiple recipients
Examples: "alice@example.com" OR ["alice@example.com", "bob@example.com"]
- subject: Email subject line
- body: Email body content

CRITICAL: Always format email bodies with proper paragraph spacing. Insert \n\n (double newline) between each paragraph or separate thought. Single-paragraph emails are difficult to read.
Note: A signature is automatically appended to all emails. Do not include signature placeholders like "[Your Name]" in the body.

IMPORTANT WORKFLOW:
1. Before calling this tool, draft the email and show it to the user
2. Ask "Should I send this email? Reply 'yes' to confirm."
3. Only call send_email() after the user explicitly confirms with 'yes' or 'send'

Never send emails without user confirmation unless the user explicitly requests it.

To call a tool, use this EXACT format:
```tool_code
send_email(to="recipient@example.com", subject="Subject", body="Body text")
```

EXAMPLE:
User: Send an email to john@example.com with subject "Hello"
Assistant: [drafts and gets confirmation]
User: yes
Assistant:
```tool_code
send_email(to="john@example.com", subject="Hello", body="The body text")
```

Use these tools when they can help answer the user's question.
Loading
Loading