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

@easyonward/mcp-server

Package Overview
Dependencies
Maintainers
1
Versions
4
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@easyonward/mcp-server - npm Package Compare versions

Comparing version
0.1.2
to
0.2.0
+10
dist/http-server.d.ts
import { type Server } from "node:http";
/**
* Build the hosted HTTP server exposing the EasyOnward MCP tools over the
* Streamable HTTP transport in **stateless** mode (read-only, no auth, no
* sessions): each POST /mcp gets a fresh server + transport, so there's no
* session store and no long-lived SSE stream to hold open. GET /health is a
* plain liveness probe. Returns the http.Server without listening, so tests can
* bind an ephemeral port.
*/
export declare function buildHttpServer(): Server;
import { createServer as createHttpServer, } from "node:http";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { createServer } from "./server.js";
const MAX_BODY_BYTES = 256 * 1024;
function setCors(res) {
res.setHeader("Access-Control-Allow-Origin", "*");
res.setHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS");
res.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, mcp-session-id, mcp-protocol-version, last-event-id");
res.setHeader("Access-Control-Expose-Headers", "mcp-session-id");
}
async function readJsonBody(req) {
const chunks = [];
let size = 0;
for await (const chunk of req) {
size += chunk.length;
if (size > MAX_BODY_BYTES)
throw new Error("Request body too large");
chunks.push(chunk);
}
const raw = Buffer.concat(chunks).toString("utf8");
return raw.length ? JSON.parse(raw) : undefined;
}
function jsonRpcError(res, status, code, message) {
if (res.headersSent)
return;
res.writeHead(status, { "Content-Type": "application/json" });
res.end(JSON.stringify({ jsonrpc: "2.0", error: { code, message }, id: null }));
}
/**
* Build the hosted HTTP server exposing the EasyOnward MCP tools over the
* Streamable HTTP transport in **stateless** mode (read-only, no auth, no
* sessions): each POST /mcp gets a fresh server + transport, so there's no
* session store and no long-lived SSE stream to hold open. GET /health is a
* plain liveness probe. Returns the http.Server without listening, so tests can
* bind an ephemeral port.
*/
export function buildHttpServer() {
return createHttpServer(async (req, res) => {
setCors(res);
if (req.method === "OPTIONS") {
res.writeHead(204).end();
return;
}
const url = new URL(req.url ?? "/", `http://${req.headers.host ?? "localhost"}`);
if (req.method === "GET" && url.pathname === "/health") {
res.writeHead(200, { "Content-Type": "application/json" });
res.end(JSON.stringify({
status: "ok",
service: "easyonward-mcp",
transport: "streamable-http",
}));
return;
}
if (req.method === "POST" && url.pathname === "/mcp") {
const server = createServer();
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined, // stateless
enableJsonResponse: true, // plain JSON responses (no SSE stream)
});
res.on("close", () => {
void transport.close();
void server.close();
});
try {
const body = await readJsonBody(req);
await server.connect(transport);
await transport.handleRequest(req, res, body);
}
catch (err) {
process.stderr.write(`MCP request error: ${err instanceof Error ? err.stack : String(err)}\n`);
jsonRpcError(res, 500, -32603, "Internal server error");
}
return;
}
// Stateless mode has no long-lived GET stream or session DELETE.
jsonRpcError(res, 405, -32000, "Method not allowed. Use POST /mcp for the MCP endpoint, or GET /health.");
});
}
//# sourceMappingURL=http-server.js.map
{"version":3,"file":"http-server.js","sourceRoot":"","sources":["../src/http-server.ts"],"names":[],"mappings":"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"}
export {};
import { buildHttpServer } from "./http-server.js";
/**
* Hosted HTTP entry point — runs the EasyOnward MCP server over Streamable HTTP
* so web-based AI clients can use it with no local install. Intended to run as a
* container behind a reverse proxy at https://mcp.easyonward.com/mcp. The npm
* package keeps stdio (index.ts) for desktop clients.
*/
const PORT = Number(process.env.PORT ?? 8200);
const server = buildHttpServer();
server.listen(PORT, () => {
process.stderr.write(`EasyOnward MCP server (Streamable HTTP) listening on :${PORT}\n`);
});
//# sourceMappingURL=http.js.map
{"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,IAAI,IAAI,CAAC,CAAC;AAE9C,MAAM,MAAM,GAAG,eAAe,EAAE,CAAC;AACjC,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;IACvB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,yDAAyD,IAAI,IAAI,CAClE,CAAC;AACJ,CAAC,CAAC,CAAC"}
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
/** Kept in sync with package.json `version`. */
export declare const SERVER_VERSION = "0.2.0";
/**
* Build a fully-configured EasyOnward MCP server with the read-only tools.
* Shared by the stdio entry point (index.ts) and the HTTP entry point
* (http.ts) so both transports expose an identical tool surface.
*/
export declare function createServer(): McpServer;
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { z } from "zod";
import { checkVisa } from "./visa.js";
import { resolveCountry } from "./countries.js";
/** Kept in sync with package.json `version`. */
export const SERVER_VERSION = "0.2.0";
/**
* Build a fully-configured EasyOnward MCP server with the read-only tools.
* Shared by the stdio entry point (index.ts) and the HTTP entry point
* (http.ts) so both transports expose an identical tool surface.
*/
export function createServer() {
const server = new McpServer({
name: "easyonward",
version: SERVER_VERSION,
});
server.registerTool("check_visa", {
title: "Check visa & entry requirements",
description: "Check whether a traveler holding a given passport needs a visa, and " +
"what entry & transit requirements apply, for a destination country — " +
"with official government sources. Accepts ISO 3166-1 alpha-2 codes " +
'(e.g. "US", "KE") or common country names (e.g. "United States", ' +
'"Kenya"). Returns a plain-language verdict, the specific requirements ' +
"by severity, deduped official source links, and a deep link to the " +
"full EasyOnward analysis. Read-only and PII-free; the analysis is " +
"generic for any citizen of the passport country.",
inputSchema: {
passport_country: z
.string()
.describe('The traveler\'s passport-issuing country. ISO 3166-1 alpha-2 code (e.g. "US") or country name (e.g. "United States").'),
destination_country: z
.string()
.describe('The destination country. ISO 3166-1 alpha-2 code (e.g. "KE") or country name (e.g. "Kenya").'),
},
}, async ({ passport_country, destination_country }) => {
const result = await checkVisa(passport_country, destination_country);
if (!result.ok) {
return {
content: [{ type: "text", text: result.text }],
structuredContent: { ok: false, error: result.error },
isError: true,
};
}
const { text, ...structured } = result;
return {
content: [{ type: "text", text }],
structuredContent: structured,
};
});
server.registerTool("country_code", {
title: "Resolve a country name to its ISO-2 code",
description: "Resolve a country name (or an existing ISO 3166-1 alpha-2 code) to its " +
'uppercase ISO 3166-1 alpha-2 code — e.g. "Kenya" → "KE". Useful before ' +
"calling check_visa. Returns an error if the name can't be resolved.",
inputSchema: {
name: z
.string()
.describe('A country name or ISO-2 code, e.g. "Kenya" or "KE".'),
},
}, async ({ name }) => {
const code = resolveCountry(name);
if (!code) {
const msg = `Could not resolve "${name}" to a country code. ` +
`Try a common country name (e.g. "Kenya") or an ISO 3166-1 ` +
`alpha-2 code (e.g. "KE").`;
return {
content: [{ type: "text", text: msg }],
structuredContent: { ok: false, error: msg },
isError: true,
};
}
return {
content: [{ type: "text", text: code }],
structuredContent: { ok: true, name, code },
};
});
return server;
}
//# sourceMappingURL=server.js.map
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
+8
-68
#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { checkVisa } from "./visa.js";
import { resolveCountry } from "./countries.js";
const server = new McpServer({
name: "easyonward",
version: "0.1.0",
});
server.registerTool("check_visa", {
title: "Check visa & entry requirements",
description: "Check whether a traveler holding a given passport needs a visa, and " +
"what entry & transit requirements apply, for a destination country — " +
"with official government sources. Accepts ISO 3166-1 alpha-2 codes " +
'(e.g. "US", "KE") or common country names (e.g. "United States", ' +
'"Kenya"). Returns a plain-language verdict, the specific requirements ' +
"by severity, deduped official source links, and a deep link to the " +
"full EasyOnward analysis. Read-only and PII-free; the analysis is " +
"generic for any citizen of the passport country.",
inputSchema: {
passport_country: z
.string()
.describe('The traveler\'s passport-issuing country. ISO 3166-1 alpha-2 code (e.g. "US") or country name (e.g. "United States").'),
destination_country: z
.string()
.describe('The destination country. ISO 3166-1 alpha-2 code (e.g. "KE") or country name (e.g. "Kenya").'),
},
}, async ({ passport_country, destination_country }) => {
const result = await checkVisa(passport_country, destination_country);
if (!result.ok) {
return {
content: [{ type: "text", text: result.text }],
structuredContent: { ok: false, error: result.error },
isError: true,
};
}
const { text, ...structured } = result;
return {
content: [{ type: "text", text }],
structuredContent: structured,
};
});
server.registerTool("country_code", {
title: "Resolve a country name to its ISO-2 code",
description: "Resolve a country name (or an existing ISO 3166-1 alpha-2 code) to its " +
'uppercase ISO 3166-1 alpha-2 code — e.g. "Kenya" → "KE". Useful before ' +
"calling check_visa. Returns an error if the name can't be resolved.",
inputSchema: {
name: z
.string()
.describe('A country name or ISO-2 code, e.g. "Kenya" or "KE".'),
},
}, async ({ name }) => {
const code = resolveCountry(name);
if (!code) {
const msg = `Could not resolve "${name}" to a country code. ` +
`Try a common country name (e.g. "Kenya") or an ISO 3166-1 ` +
`alpha-2 code (e.g. "KE").`;
return {
content: [{ type: "text", text: msg }],
structuredContent: { ok: false, error: msg },
isError: true,
};
}
return {
content: [{ type: "text", text: code }],
structuredContent: { ok: true, name, code },
};
});
import { createServer } from "./server.js";
/**
* stdio entry point — the default for the npm package (`npx -y
* @easyonward/mcp-server`). Desktop/IDE clients (Claude Desktop, Cursor,
* Windsurf, Cline) spawn this and speak MCP over stdio. For the hosted HTTP
* transport, see http.ts.
*/
async function main() {
const server = createServer();
const transport = new StdioServerTransport();

@@ -73,0 +13,0 @@ await server.connect(transport);

@@ -1,1 +0,1 @@

{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAE3C;;;;;GAKG;AACH,KAAK,UAAU,IAAI;IACjB,MAAM,MAAM,GAAG,YAAY,EAAE,CAAC;IAC9B,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,qEAAqE;IACrE,yCAAyC;IACzC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,0CAA0C,CAAC,CAAC;AACnE,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAClB,+CACE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAC/C,IAAI,CACL,CAAC;IACF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
{
"name": "@easyonward/mcp-server",
"mcpName": "com.easyonward/mcp-server",
"version": "0.1.2",
"version": "0.2.0",
"description": "Read-only Model Context Protocol server for EasyOnward \u2014 check passport visa, entry, and transit requirements with official government sources.",

@@ -36,3 +36,4 @@ "license": "MIT",

"start": "node dist/index.js",
"test": "node --test --import tsx test/*.test.ts"
"test": "node --test --import tsx test/*.test.ts",
"start:http": "node dist/http.js"
},

@@ -39,0 +40,0 @@ "keywords": [

@@ -70,12 +70,38 @@ # EasyOnward MCP Server

Runs on Node 18+ via `npx` (no install needed):
Two ways to connect, same tools:
- **Hosted (remote)** — no install, nothing to run. Point any HTTP-capable MCP
client at `https://mcp.easyonward.com/mcp` (Streamable HTTP). Best for most
users.
- **Local (stdio)** — runs on Node 18+ via `npx` (no install needed):
```bash
npx -y @easyonward/mcp-server
```
The server speaks MCP over **stdio**. Best for offline/air-gapped setups or
clients without remote transport.
### Hosted (remote) — Claude Code / any HTTP client
```bash
npx -y @easyonward/mcp-server
claude mcp add --transport http easyonward https://mcp.easyonward.com/mcp
```
The server speaks MCP over **stdio**.
Or in a client config that supports remote servers:
### Claude Desktop
```json
{
"mcpServers": {
"easyonward": {
"type": "streamable-http",
"url": "https://mcp.easyonward.com/mcp"
}
}
}
```
The hosted server is read-only, stateless, and PII-free — same two tools as
the local build.
### Claude Desktop (local stdio)
Add to `claude_desktop_config.json`

@@ -122,2 +148,9 @@ (macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`,

### Windsurf, Cline & other stdio clients
Any client that supports local stdio MCP servers uses the **same block** —
`command: npx`, `args: ["-y", "@easyonward/mcp-server"]`. In Windsurf it's
*Settings → Cascade → MCP Servers*; in Cline it's the MCP settings JSON. No
client-specific setup.
## Configuration

@@ -143,2 +176,16 @@

## Security & privacy
- **Read-only.** The server only performs GET lookups against the public EasyOnward
API — it never writes, books, or mutates anything.
- **No secrets, no auth.** There's no API key or token to configure, so there's
nothing to leak.
- **No PII.** It sends only a passport country + a destination country (ISO-2 codes
or country names). It never asks for or transmits passport numbers, names, dates
of birth, or any personal data.
- **Public data only.** Everything returned is public visa/entry/transit reference
information, with links to official government sources.
- **Fail-safe.** An unreachable API or an uncatalogued country pair returns a clear
message, not a crash. Requests are subject to the public API's rate limits.
## Develop

@@ -145,0 +192,0 @@