client.ts•5.58 kB
#!/usr/bin/env node
/**
* TMB Bus MCP Client
*
* This client connects to the TMB Bus MCP server and demonstrates
* how to use the available tools to query bus arrival times.
*/
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
import { spawn } from 'child_process';
import { fileURLToPath } from 'url';
import { dirname, join } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
class TMBBusClient {
private client: Client;
private transport: StdioClientTransport | null = null;
constructor() {
this.client = new Client(
{
name: 'tmb-bus-client',
version: '1.0.0',
},
{
capabilities: {},
}
);
}
/**
* Connect to the MCP server
*/
async connect() {
// Check for required environment variables
if (!process.env.TMB_APP_ID || !process.env.TMB_APP_KEY) {
throw new Error(
'TMB_APP_ID and TMB_APP_KEY environment variables must be set'
);
}
const serverPath = join(__dirname, 'server.js');
// Start the server as a child process
const serverProcess = spawn('node', [serverPath], {
env: {
...process.env,
TMB_APP_ID: process.env.TMB_APP_ID,
TMB_APP_KEY: process.env.TMB_APP_KEY,
},
});
// Create transport using the server process stdio
this.transport = new StdioClientTransport({
command: 'node',
args: [serverPath],
env: {
TMB_APP_ID: process.env.TMB_APP_ID,
TMB_APP_KEY: process.env.TMB_APP_KEY,
},
});
await this.client.connect(this.transport);
console.log('✅ Connected to TMB Bus MCP Server\n');
}
/**
* List available tools
*/
async listTools() {
const response = await this.client.listTools();
console.log('📋 Available Tools:');
console.log('â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”â”\n');
for (const tool of response.tools) {
console.log(`🔧 ${tool.name}`);
console.log(` ${tool.description}\n`);
console.log(` Input Schema:`);
console.log(` ${JSON.stringify(tool.inputSchema, null, 2)}\n`);
}
}
/**
* Get bus arrivals for a stop
*/
async getBusArrivals(stopCode: string) {
console.log(`Querying bus arrivals for stop ${stopCode}...\n`);
try {
const response = await this.client.callTool({
name: 'get_bus_arrivals',
arguments: { stopCode },
});
if (response.isError) {
console.error('Error:', response);
return;
}
const content = Array.isArray(response.content)
? (response.content as unknown[])
: [];
type TextContent = { type: 'text'; text: string };
const textChunks = content
.filter((item): item is TextContent => {
if (typeof item !== 'object' || item === null) {
return false;
}
const maybeItem = item as { type?: unknown; text?: unknown };
return maybeItem.type === 'text' && typeof maybeItem.text === 'string';
})
.map((item) => item.text.trim())
.filter((text) => text.length > 0);
if (textChunks.length > 0) {
console.log(textChunks.join('\n\n'));
} else if (content.length > 0) {
console.log('Received non-text content:');
console.log(JSON.stringify(response.content, null, 2));
} else {
console.log('No content returned from get_bus_arrivals.');
}
} catch (error) {
console.error('Error calling tool:', error);
}
}
/**
* Disconnect from the server
*/
async disconnect() {
await this.client.close();
console.log('\n✅ Disconnected from server');
}
}
/**
* Main function - demonstrates client usage
*/
async function main() {
// Parse command line arguments
const args = process.argv.slice(2);
if (args.length === 0 || args[0] === '--help') {
console.log('TMB Bus MCP Client\n');
console.log('Usage:');
console.log(' npm run client <stop_code> Query a specific bus stop');
console.log(' npm run client -- list List available tools');
console.log(' npm run client -- help Show this help\n');
console.log('Examples:');
console.log(' npm run client 2775 Get arrivals for stop 2775');
console.log(' npm run client 108 Get arrivals for stop 108');
console.log(' npm run client -- list List all available tools\n');
console.log('Note: Set TMB_APP_ID and TMB_APP_KEY environment variables before running\n');
process.exit(0);
}
const client = new TMBBusClient();
try {
await client.connect();
// Handle different commands
if (args[0] === 'list') {
await client.listTools();
} else {
// Assume it's a stop code
const stopCode = args[0];
await client.getBusArrivals(stopCode);
}
} catch (error) {
console.error('⌠Error:', error instanceof Error ? error.message : error);
process.exit(1);
} finally {
await client.disconnect();
}
}
// Run the client
main().catch(console.error);