Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 4 additions & 2 deletions src/agent-card.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
* discovered and invoked by other agents (Gary, n8n workflows, Cursor, etc.)
*/

import { AGENT_CONTACT_URL, AGENT_SOURCE_URL } from "./constants.js";

export interface AgentCard {
name: string;
description: string;
Expand Down Expand Up @@ -98,8 +100,8 @@ export function buildAgentCard(baseUrl: string): AgentCard {
},
],
},
contact: "https://github.com/Open-Paws",
contact: AGENT_CONTACT_URL,
license: "GPL-3.0-only",
source: "https://github.com/Open-Paws/mcp-server-nav-language",
source: AGENT_SOURCE_URL,
};
}
10 changes: 10 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* Shared constants — URLs and metadata for the NAV Language Checker server.
*/

export const UPSTREAM_RULES_URL =
"https://raw.githubusercontent.com/Open-Paws/semgrep-rules-no-animal-violence/main/rules/animal-violence-generic.yaml";

export const AGENT_CONTACT_URL = "https://github.com/Open-Paws";
export const AGENT_SOURCE_URL =
"https://github.com/Open-Paws/mcp-server-nav-language";
12 changes: 6 additions & 6 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ async function main(): Promise<void> {
// Load rules: embedded set + optional upstream merge
const rules = await loadRules();
const engine = new NavEngine(rules);
console.log(`[nav] Engine ready with ${engine.ruleCount} rules.`);
console.error(`[nav] Engine ready with ${engine.ruleCount} rules.`);

// Create MCP server
const server = new McpServer({
Expand Down Expand Up @@ -142,19 +142,19 @@ async function startHttpMode(
});

httpServer.listen(port, () => {
console.log(`[nav] HTTP server listening on port ${port}`);
console.log(`[nav] Agent Card: ${baseUrl}/.well-known/agent.json`);
console.log(`[nav] Health: ${baseUrl}/health`);
console.error(`[nav] HTTP server listening on port ${port}`);
console.error(`[nav] Agent Card: ${baseUrl}/.well-known/agent.json`);
console.error(`[nav] Health: ${baseUrl}/health`);
});

// Keep process alive
await new Promise<void>((resolve) => {
process.on("SIGTERM", () => {
console.log("[nav] SIGTERM received. Stopping.");
console.error("[nav] SIGTERM received. Stopping.");
httpServer.close(() => resolve());
});
process.on("SIGINT", () => {
console.log("[nav] SIGINT received. Stopping.");
console.error("[nav] SIGINT received. Stopping.");
httpServer.close(() => resolve());
});
});
Expand Down
14 changes: 6 additions & 8 deletions src/rule-loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@

import { parse as parseYaml } from "yaml";
import { EMBEDDED_RULES, type NavRule, type Severity } from "./rules.js";

const UPSTREAM_YAML_URL =
"https://raw.githubusercontent.com/Open-Paws/semgrep-rules-no-animal-violence/main/rules/animal-violence-generic.yaml";
import { UPSTREAM_RULES_URL } from "./constants.js";

interface SemgrepRule {
id: string;
Expand Down Expand Up @@ -68,13 +66,13 @@ export async function loadRules(): Promise<NavRule[]> {
const controller = new AbortController();
const timeout = setTimeout(() => controller.abort(), 3000);

const response = await fetch(UPSTREAM_YAML_URL, {
const response = await fetch(UPSTREAM_RULES_URL, {
signal: controller.signal,
});
clearTimeout(timeout);

if (!response.ok) {
console.warn(
console.error(
`[nav] Upstream rule fetch returned ${response.status}. Using embedded rules only.`
);
return [...EMBEDDED_RULES];
Expand All @@ -86,19 +84,19 @@ export async function loadRules(): Promise<NavRule[]> {
// Merge: add upstream rules not already covered by id
const newRules = upstreamRules.filter((r) => !embeddedIds.has(r.id));
if (newRules.length > 0) {
console.log(
console.error(
`[nav] Loaded ${upstreamRules.length} upstream rules, merged ${newRules.length} new.`
);
} else {
console.log(
console.error(
`[nav] Upstream rules fetched. ${EMBEDDED_RULES.length} embedded rules are current.`
);
}

return [...EMBEDDED_RULES, ...newRules];
} catch (err) {
const message = err instanceof Error ? err.message : String(err);
console.warn(
console.error(
`[nav] Could not fetch upstream rules (${message}). Using embedded rules only.`
);
return [...EMBEDDED_RULES];
Expand Down
Loading